ha-mcp-dev 7.6.0.dev619__tar.gz → 7.6.0.dev621__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.6.0.dev619/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.6.0.dev621}/PKG-INFO +2 -2
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/README.md +1 -1
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/pyproject.toml +1 -1
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_config_automations.py +5 -8
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_config_helpers.py +7 -15
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_config_scenes.py +5 -9
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_config_scripts.py +4 -7
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_entities.py +11 -52
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_filesystem.py +12 -28
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_groups.py +4 -9
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_hacs.py +15 -32
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_history.py +15 -97
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_integrations.py +43 -82
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_registry.py +7 -7
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_search.py +43 -76
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_service.py +8 -17
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_services.py +7 -7
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_system.py +14 -37
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_updates.py +5 -14
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_utility.py +38 -82
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_yaml_config.py +3 -6
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/util_helpers.py +1 -138
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621/src/ha_mcp_dev.egg-info}/PKG-INFO +2 -2
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/LICENSE +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/setup.cfg +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/_version.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/backup_manager.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/client/rest_client.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/config.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/policy/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/policy/approval_queue.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/policy/evaluator.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/policy/handlers.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/policy/middleware.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/policy/model.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/policy/persistence.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/policy/value_sources.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/settings_ui.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/auto_backup.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/smart_search.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/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.6.0.
|
|
3
|
+
Version: 7.6.0.dev621
|
|
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
|
|
@@ -259,7 +259,7 @@ An MCP server can create automations, helpers, and dashboards, but it has no opi
|
|
|
259
259
|
|
|
260
260
|
Skills from `homeassistant-ai/skills` are bundled and served as [MCP resources](https://modelcontextprotocol.io/docs/concepts/resources) via `skill://` URIs. Any MCP client that supports resources can discover them automatically — no manual installation needed. For tool-only clients (claude.ai, etc.), the same skills are reachable through the polymorphic `ha_get_skill_guide` tool — call it with no args to list bundled skills, with a `skill` arg to list its files, or with `skill` + `file` to read content. Resources are not auto-injected into context — clients must explicitly request them, so idle context cost is just the metadata listing.
|
|
261
261
|
|
|
262
|
-
`ha_get_skill_guide` is mandatory
|
|
262
|
+
`ha_get_skill_guide` is a mandatory tool: the catalog always exposes it (it can't be disabled) so tool-only clients never see a silently missing skill surface.
|
|
263
263
|
|
|
264
264
|
Skills can still be installed manually for clients that prefer local skill files — see the [skills repo](https://github.com/homeassistant-ai/skills) for instructions.
|
|
265
265
|
|
|
@@ -229,7 +229,7 @@ An MCP server can create automations, helpers, and dashboards, but it has no opi
|
|
|
229
229
|
|
|
230
230
|
Skills from `homeassistant-ai/skills` are bundled and served as [MCP resources](https://modelcontextprotocol.io/docs/concepts/resources) via `skill://` URIs. Any MCP client that supports resources can discover them automatically — no manual installation needed. For tool-only clients (claude.ai, etc.), the same skills are reachable through the polymorphic `ha_get_skill_guide` tool — call it with no args to list bundled skills, with a `skill` arg to list its files, or with `skill` + `file` to read content. Resources are not auto-injected into context — clients must explicitly request them, so idle context cost is just the metadata listing.
|
|
231
231
|
|
|
232
|
-
`ha_get_skill_guide` is mandatory
|
|
232
|
+
`ha_get_skill_guide` is a mandatory tool: the catalog always exposes it (it can't be disabled) so tool-only clients never see a silently missing skill surface.
|
|
233
233
|
|
|
234
234
|
Skills can still be installed manually for clients that prefer local skill files — see the [skills repo](https://github.com/homeassistant-ai/skills) for instructions.
|
|
235
235
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ha-mcp-dev"
|
|
7
|
-
version = "7.6.0.
|
|
7
|
+
version = "7.6.0.dev621"
|
|
8
8
|
description = "Home Assistant MCP Server - Complete control of Home Assistant through MCP"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.13,<3.14"
|
{ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_config_automations.py
RENAMED
|
@@ -44,7 +44,6 @@ from .helpers import (
|
|
|
44
44
|
from .reference_validator import validate_config_references
|
|
45
45
|
from .util_helpers import (
|
|
46
46
|
apply_entity_category,
|
|
47
|
-
coerce_bool_param,
|
|
48
47
|
coerce_to_list,
|
|
49
48
|
fetch_entity_category,
|
|
50
49
|
merge_validation_meta,
|
|
@@ -447,7 +446,7 @@ class AutomationConfigTools:
|
|
|
447
446
|
),
|
|
448
447
|
] = None,
|
|
449
448
|
wait: Annotated[
|
|
450
|
-
bool
|
|
449
|
+
bool,
|
|
451
450
|
Field(
|
|
452
451
|
description="Wait for automation to be queryable before returning. Default: True. Set to False for bulk operations.",
|
|
453
452
|
default=True,
|
|
@@ -829,7 +828,7 @@ class AutomationConfigTools:
|
|
|
829
828
|
config_dict: dict[str, Any],
|
|
830
829
|
identifier: str | None,
|
|
831
830
|
effective_category: str | None,
|
|
832
|
-
wait: bool
|
|
831
|
+
wait: bool,
|
|
833
832
|
bp_warnings: list[str],
|
|
834
833
|
validation_meta: dict[str, Any],
|
|
835
834
|
) -> dict[str, Any]:
|
|
@@ -846,11 +845,10 @@ class AutomationConfigTools:
|
|
|
846
845
|
)
|
|
847
846
|
result.pop("entity_not_verified", None)
|
|
848
847
|
|
|
849
|
-
wait_bool = coerce_bool_param(wait, "wait", default=True)
|
|
850
848
|
entity_id = result.get("entity_id")
|
|
851
849
|
if not entity_id and identifier and identifier.startswith("automation."):
|
|
852
850
|
entity_id = identifier
|
|
853
|
-
if
|
|
851
|
+
if wait and entity_id:
|
|
854
852
|
action_word = "created" if identifier is None else "updated"
|
|
855
853
|
try:
|
|
856
854
|
registered = await wait_for_entity_registered(self._client, entity_id)
|
|
@@ -1167,7 +1165,7 @@ class AutomationConfigTools:
|
|
|
1167
1165
|
),
|
|
1168
1166
|
],
|
|
1169
1167
|
wait: Annotated[
|
|
1170
|
-
bool
|
|
1168
|
+
bool,
|
|
1171
1169
|
Field(
|
|
1172
1170
|
description="Wait for automation to be fully removed before returning. Default: True.",
|
|
1173
1171
|
default=True,
|
|
@@ -1208,8 +1206,7 @@ class AutomationConfigTools:
|
|
|
1208
1206
|
result = await self._client.delete_automation_config(identifier)
|
|
1209
1207
|
|
|
1210
1208
|
# Wait for entity to be removed
|
|
1211
|
-
|
|
1212
|
-
if wait_bool and entity_id_for_wait:
|
|
1209
|
+
if wait and entity_id_for_wait:
|
|
1213
1210
|
try:
|
|
1214
1211
|
removed = await wait_for_entity_removed(
|
|
1215
1212
|
self._client, entity_id_for_wait
|
{ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_config_helpers.py
RENAMED
|
@@ -33,7 +33,6 @@ from .tools_config_entry_flow import (
|
|
|
33
33
|
)
|
|
34
34
|
from .util_helpers import (
|
|
35
35
|
apply_entity_category,
|
|
36
|
-
coerce_bool_param,
|
|
37
36
|
parse_json_param,
|
|
38
37
|
parse_string_list_param,
|
|
39
38
|
wait_for_entity_registered,
|
|
@@ -1553,7 +1552,7 @@ async def _handle_flow_helper(
|
|
|
1553
1552
|
area_id: str | None,
|
|
1554
1553
|
labels: str | list[str] | None,
|
|
1555
1554
|
category: str | None,
|
|
1556
|
-
wait: bool
|
|
1555
|
+
wait: bool,
|
|
1557
1556
|
action: str | None = None,
|
|
1558
1557
|
) -> dict[str, Any]:
|
|
1559
1558
|
"""Create or update a flow-based helper and apply registry updates to all entities.
|
|
@@ -1737,10 +1736,9 @@ async def _handle_flow_helper(
|
|
|
1737
1736
|
# instances quickly; steady 500ms matches typical entity_registry/list latency
|
|
1738
1737
|
# on larger remote setups without missing entities near the deadline.
|
|
1739
1738
|
warnings: list[str] = list(pre_warnings)
|
|
1740
|
-
wait_bool = coerce_bool_param(wait, "wait", default=True)
|
|
1741
1739
|
entities: list[dict[str, Any]] = []
|
|
1742
1740
|
if entry_id:
|
|
1743
|
-
if action == "create" and
|
|
1741
|
+
if action == "create" and wait:
|
|
1744
1742
|
deadline = 5.0
|
|
1745
1743
|
intervals = [0.2, 0.3] # first two retries faster
|
|
1746
1744
|
steady_interval = 0.5
|
|
@@ -2070,7 +2068,7 @@ def register_config_helper_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
2070
2068
|
),
|
|
2071
2069
|
] = None,
|
|
2072
2070
|
show_advanced_options: Annotated[
|
|
2073
|
-
bool
|
|
2071
|
+
bool,
|
|
2074
2072
|
Field(
|
|
2075
2073
|
description=(
|
|
2076
2074
|
"When helper_type='config_subentry', ask Home Assistant "
|
|
@@ -2312,7 +2310,7 @@ def register_config_helper_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
2312
2310
|
),
|
|
2313
2311
|
] = None,
|
|
2314
2312
|
wait: Annotated[
|
|
2315
|
-
bool
|
|
2313
|
+
bool,
|
|
2316
2314
|
Field(
|
|
2317
2315
|
description="Wait for helper entity to be queryable before returning. Default: True. Set to False for bulk operations.",
|
|
2318
2316
|
default=True,
|
|
@@ -2484,11 +2482,7 @@ def register_config_helper_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
2484
2482
|
subentry_type,
|
|
2485
2483
|
config_dict,
|
|
2486
2484
|
subentry_id=subentry_id,
|
|
2487
|
-
show_advanced_options=
|
|
2488
|
-
show_advanced_options,
|
|
2489
|
-
"show_advanced_options",
|
|
2490
|
-
default=False,
|
|
2491
|
-
),
|
|
2485
|
+
show_advanced_options=show_advanced_options,
|
|
2492
2486
|
)
|
|
2493
2487
|
|
|
2494
2488
|
# Determine if this is a create or update — set early so the
|
|
@@ -2975,8 +2969,7 @@ def register_config_helper_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
2975
2969
|
# branch under the update action below already skips the
|
|
2976
2970
|
# wait for this reason; mirror it here so create doesn't
|
|
2977
2971
|
# burn 10s per tag on every CI run.
|
|
2978
|
-
|
|
2979
|
-
if wait_bool and entity_id and helper_type != "tag":
|
|
2972
|
+
if wait and entity_id and helper_type != "tag":
|
|
2980
2973
|
try:
|
|
2981
2974
|
registered = await wait_for_entity_registered(
|
|
2982
2975
|
client, entity_id
|
|
@@ -3725,8 +3718,7 @@ def register_config_helper_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
3725
3718
|
warnings.extend(cat_result["warnings"])
|
|
3726
3719
|
|
|
3727
3720
|
# Wait for entity to reflect the update
|
|
3728
|
-
|
|
3729
|
-
if wait_bool:
|
|
3721
|
+
if wait:
|
|
3730
3722
|
try:
|
|
3731
3723
|
registered = await wait_for_entity_registered(client, entity_id)
|
|
3732
3724
|
if not registered:
|
|
@@ -39,7 +39,6 @@ from .helpers import (
|
|
|
39
39
|
from .reference_validator import validate_config_references
|
|
40
40
|
from .util_helpers import (
|
|
41
41
|
apply_entity_category,
|
|
42
|
-
coerce_bool_param,
|
|
43
42
|
fetch_entity_category,
|
|
44
43
|
merge_validation_meta,
|
|
45
44
|
parse_json_param,
|
|
@@ -472,7 +471,7 @@ class ConfigSceneTools:
|
|
|
472
471
|
),
|
|
473
472
|
] = None,
|
|
474
473
|
wait: Annotated[
|
|
475
|
-
bool
|
|
474
|
+
bool,
|
|
476
475
|
Field(
|
|
477
476
|
description=(
|
|
478
477
|
"Wait for scene to be queryable before returning. Default: True. "
|
|
@@ -723,9 +722,8 @@ class ConfigSceneTools:
|
|
|
723
722
|
# post-upsert finalisation the full-config branch runs. Without
|
|
724
723
|
# these, ``wait`` and ``category`` are silently dropped on
|
|
725
724
|
# python_transform calls.
|
|
726
|
-
wait_bool = coerce_bool_param(wait, "wait", default=True)
|
|
727
725
|
entity_id = await self._resolve_scene_entity_id(resolved_id)
|
|
728
|
-
if
|
|
726
|
+
if wait:
|
|
729
727
|
try:
|
|
730
728
|
registered = await wait_for_entity_registered(
|
|
731
729
|
self._client, entity_id
|
|
@@ -841,8 +839,7 @@ class ConfigSceneTools:
|
|
|
841
839
|
entity_id = await self._resolve_scene_entity_id(resolved_id)
|
|
842
840
|
|
|
843
841
|
# Wait for scene to be queryable
|
|
844
|
-
|
|
845
|
-
if wait_bool:
|
|
842
|
+
if wait:
|
|
846
843
|
try:
|
|
847
844
|
registered = await wait_for_entity_registered(
|
|
848
845
|
self._client, entity_id
|
|
@@ -914,7 +911,7 @@ class ConfigSceneTools:
|
|
|
914
911
|
str, Field(description="Scene identifier to delete (e.g., 'old_scene')")
|
|
915
912
|
],
|
|
916
913
|
wait: Annotated[
|
|
917
|
-
bool
|
|
914
|
+
bool,
|
|
918
915
|
Field(
|
|
919
916
|
description="Wait for scene to be fully removed before returning. Default: True.",
|
|
920
917
|
default=True,
|
|
@@ -970,8 +967,7 @@ class ConfigSceneTools:
|
|
|
970
967
|
|
|
971
968
|
result = await self._client.delete_scene_config(resolved_id)
|
|
972
969
|
|
|
973
|
-
|
|
974
|
-
if wait_bool:
|
|
970
|
+
if wait:
|
|
975
971
|
try:
|
|
976
972
|
removed = await wait_for_entity_removed(self._client, entity_id)
|
|
977
973
|
if not removed:
|
{ha_mcp_dev-7.6.0.dev619 → ha_mcp_dev-7.6.0.dev621}/src/ha_mcp/tools/tools_config_scripts.py
RENAMED
|
@@ -39,7 +39,6 @@ from .helpers import (
|
|
|
39
39
|
from .reference_validator import validate_config_references
|
|
40
40
|
from .util_helpers import (
|
|
41
41
|
apply_entity_category,
|
|
42
|
-
coerce_bool_param,
|
|
43
42
|
fetch_entity_category,
|
|
44
43
|
merge_validation_meta,
|
|
45
44
|
parse_json_param,
|
|
@@ -421,7 +420,7 @@ class ConfigScriptTools:
|
|
|
421
420
|
),
|
|
422
421
|
] = None,
|
|
423
422
|
wait: Annotated[
|
|
424
|
-
bool
|
|
423
|
+
bool,
|
|
425
424
|
Field(
|
|
426
425
|
description="Wait for script to be queryable before returning. Default: True. Set to False for bulk operations.",
|
|
427
426
|
default=True,
|
|
@@ -721,9 +720,8 @@ class ConfigScriptTools:
|
|
|
721
720
|
result = await self._client.upsert_script_config(config_dict, script_id)
|
|
722
721
|
|
|
723
722
|
# Wait for script to be queryable
|
|
724
|
-
wait_bool = coerce_bool_param(wait, "wait", default=True)
|
|
725
723
|
entity_id = f"script.{script_id}"
|
|
726
|
-
if
|
|
724
|
+
if wait:
|
|
727
725
|
try:
|
|
728
726
|
registered = await wait_for_entity_registered(
|
|
729
727
|
self._client, entity_id
|
|
@@ -806,7 +804,7 @@ class ConfigScriptTools:
|
|
|
806
804
|
),
|
|
807
805
|
],
|
|
808
806
|
wait: Annotated[
|
|
809
|
-
bool
|
|
807
|
+
bool,
|
|
810
808
|
Field(
|
|
811
809
|
description="Wait for script to be fully removed before returning. Default: True.",
|
|
812
810
|
default=True,
|
|
@@ -850,9 +848,8 @@ class ConfigScriptTools:
|
|
|
850
848
|
result = await self._client.delete_script_config(script_id)
|
|
851
849
|
|
|
852
850
|
# Wait for script to be removed
|
|
853
|
-
wait_bool = coerce_bool_param(wait, "wait", default=True)
|
|
854
851
|
entity_id = f"script.{script_id}"
|
|
855
|
-
if
|
|
852
|
+
if wait:
|
|
856
853
|
try:
|
|
857
854
|
removed = await wait_for_entity_removed(self._client, entity_id)
|
|
858
855
|
if not removed:
|
|
@@ -22,7 +22,7 @@ from .helpers import (
|
|
|
22
22
|
validate_identifier_not_empty,
|
|
23
23
|
)
|
|
24
24
|
from .tools_voice_assistant import KNOWN_ASSISTANTS
|
|
25
|
-
from .util_helpers import
|
|
25
|
+
from .util_helpers import parse_json_param, parse_string_list_param
|
|
26
26
|
|
|
27
27
|
logger = logging.getLogger(__name__)
|
|
28
28
|
|
|
@@ -83,8 +83,8 @@ def register_entity_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
83
83
|
area_id: str | None,
|
|
84
84
|
name: str | None,
|
|
85
85
|
icon: str | None,
|
|
86
|
-
enabled: bool |
|
|
87
|
-
hidden: bool |
|
|
86
|
+
enabled: bool | None,
|
|
87
|
+
hidden: bool | None,
|
|
88
88
|
parsed_aliases: list[str] | None,
|
|
89
89
|
parsed_categories: dict[str, str | None] | None,
|
|
90
90
|
parsed_labels: list[str] | None,
|
|
@@ -151,30 +151,12 @@ def register_entity_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
151
151
|
)
|
|
152
152
|
|
|
153
153
|
if enabled is not None:
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
except ValueError as e:
|
|
157
|
-
raise_tool_error(
|
|
158
|
-
create_error_response(
|
|
159
|
-
ErrorCode.VALIDATION_INVALID_PARAMETER,
|
|
160
|
-
str(e),
|
|
161
|
-
)
|
|
162
|
-
)
|
|
163
|
-
message["disabled_by"] = None if enabled_bool else "user"
|
|
164
|
-
updates_made.append("enabled" if enabled_bool else "disabled")
|
|
154
|
+
message["disabled_by"] = None if enabled else "user"
|
|
155
|
+
updates_made.append("enabled" if enabled else "disabled")
|
|
165
156
|
|
|
166
157
|
if hidden is not None:
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
except ValueError as e:
|
|
170
|
-
raise_tool_error(
|
|
171
|
-
create_error_response(
|
|
172
|
-
ErrorCode.VALIDATION_INVALID_PARAMETER,
|
|
173
|
-
str(e),
|
|
174
|
-
)
|
|
175
|
-
)
|
|
176
|
-
message["hidden_by"] = "user" if hidden_bool else None
|
|
177
|
-
updates_made.append("hidden" if hidden_bool else "visible")
|
|
158
|
+
message["hidden_by"] = "user" if hidden else None
|
|
159
|
+
updates_made.append("hidden" if hidden else "visible")
|
|
178
160
|
|
|
179
161
|
if parsed_aliases is not None:
|
|
180
162
|
message["aliases"] = parsed_aliases
|
|
@@ -630,7 +612,7 @@ def register_entity_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
630
612
|
),
|
|
631
613
|
] = None,
|
|
632
614
|
enabled: Annotated[
|
|
633
|
-
bool |
|
|
615
|
+
bool | None,
|
|
634
616
|
Field(
|
|
635
617
|
description=(
|
|
636
618
|
"True to enable the entity, False to disable it. Single entity only. "
|
|
@@ -644,7 +626,7 @@ def register_entity_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
644
626
|
),
|
|
645
627
|
] = None,
|
|
646
628
|
hidden: Annotated[
|
|
647
|
-
bool |
|
|
629
|
+
bool | None,
|
|
648
630
|
Field(
|
|
649
631
|
description="True to hide the entity from UI, False to show it. Single entity only.",
|
|
650
632
|
default=None,
|
|
@@ -872,12 +854,7 @@ def register_entity_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
872
854
|
# script.turn_off) which simply prevent them from running while
|
|
873
855
|
# keeping them visible and manageable.
|
|
874
856
|
if enabled is not None:
|
|
875
|
-
|
|
876
|
-
_enabled_check = coerce_bool_param(enabled, "enabled")
|
|
877
|
-
except ValueError:
|
|
878
|
-
_enabled_check = None # will be caught by _update_single_entity
|
|
879
|
-
|
|
880
|
-
if _enabled_check is False:
|
|
857
|
+
if enabled is False:
|
|
881
858
|
blocked = [
|
|
882
859
|
eid
|
|
883
860
|
for eid in entity_ids
|
|
@@ -1030,25 +1007,7 @@ def register_entity_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
1030
1007
|
)
|
|
1031
1008
|
)
|
|
1032
1009
|
|
|
1033
|
-
#
|
|
1034
|
-
for asst, val in parsed_expose_to.items():
|
|
1035
|
-
try:
|
|
1036
|
-
coerced = coerce_bool_param(val, f"expose_to[{asst}]")
|
|
1037
|
-
except ValueError as e:
|
|
1038
|
-
raise_tool_error(
|
|
1039
|
-
create_error_response(
|
|
1040
|
-
ErrorCode.VALIDATION_INVALID_PARAMETER,
|
|
1041
|
-
str(e),
|
|
1042
|
-
)
|
|
1043
|
-
)
|
|
1044
|
-
if coerced is None:
|
|
1045
|
-
raise_tool_error(
|
|
1046
|
-
create_error_response(
|
|
1047
|
-
ErrorCode.VALIDATION_INVALID_PARAMETER,
|
|
1048
|
-
f"expose_to[{asst}] must be a boolean value",
|
|
1049
|
-
)
|
|
1050
|
-
)
|
|
1051
|
-
parsed_expose_to[asst] = coerced
|
|
1010
|
+
# Values are already bool (enforced by the dict[str, bool] annotation)
|
|
1052
1011
|
|
|
1053
1012
|
# Single entity case - use existing logic
|
|
1054
1013
|
if not is_bulk:
|
|
@@ -30,7 +30,7 @@ from .helpers import (
|
|
|
30
30
|
raise_tool_error,
|
|
31
31
|
register_tool_methods,
|
|
32
32
|
)
|
|
33
|
-
from .util_helpers import
|
|
33
|
+
from .util_helpers import unwrap_service_response
|
|
34
34
|
|
|
35
35
|
logger = logging.getLogger(__name__)
|
|
36
36
|
|
|
@@ -453,9 +453,11 @@ class FilesystemTools:
|
|
|
453
453
|
),
|
|
454
454
|
],
|
|
455
455
|
tail_lines: Annotated[
|
|
456
|
-
int |
|
|
456
|
+
int | None,
|
|
457
457
|
Field(
|
|
458
458
|
default=None,
|
|
459
|
+
ge=1,
|
|
460
|
+
le=10000,
|
|
459
461
|
description=(
|
|
460
462
|
"For log files, return only the last N lines. "
|
|
461
463
|
"Recommended for home-assistant.log to avoid large responses. "
|
|
@@ -501,22 +503,13 @@ class FilesystemTools:
|
|
|
501
503
|
```
|
|
502
504
|
"""
|
|
503
505
|
try:
|
|
504
|
-
# Coerce tail_lines parameter
|
|
505
|
-
tail_lines_int = coerce_int_param(
|
|
506
|
-
tail_lines,
|
|
507
|
-
"tail_lines",
|
|
508
|
-
default=None,
|
|
509
|
-
min_value=1,
|
|
510
|
-
max_value=10000,
|
|
511
|
-
)
|
|
512
|
-
|
|
513
506
|
# Check if custom component is available
|
|
514
507
|
await _assert_mcp_tools_available(self._client)
|
|
515
508
|
|
|
516
509
|
# Build service data
|
|
517
510
|
service_data: dict[str, Any] = {"path": path}
|
|
518
|
-
if
|
|
519
|
-
service_data["tail_lines"] =
|
|
511
|
+
if tail_lines is not None:
|
|
512
|
+
service_data["tail_lines"] = tail_lines
|
|
520
513
|
|
|
521
514
|
# Call the custom component service
|
|
522
515
|
result = await call_mcp_tools_service(
|
|
@@ -575,7 +568,7 @@ class FilesystemTools:
|
|
|
575
568
|
),
|
|
576
569
|
],
|
|
577
570
|
overwrite: Annotated[
|
|
578
|
-
bool
|
|
571
|
+
bool,
|
|
579
572
|
Field(
|
|
580
573
|
default=False,
|
|
581
574
|
description=(
|
|
@@ -585,7 +578,7 @@ class FilesystemTools:
|
|
|
585
578
|
),
|
|
586
579
|
] = False,
|
|
587
580
|
create_dirs: Annotated[
|
|
588
|
-
bool
|
|
581
|
+
bool,
|
|
589
582
|
Field(
|
|
590
583
|
default=True,
|
|
591
584
|
description=(
|
|
@@ -637,12 +630,6 @@ class FilesystemTools:
|
|
|
637
630
|
```
|
|
638
631
|
"""
|
|
639
632
|
try:
|
|
640
|
-
# Coerce boolean parameters
|
|
641
|
-
overwrite_bool = coerce_bool_param(overwrite, "overwrite", default=False)
|
|
642
|
-
create_dirs_bool = coerce_bool_param(
|
|
643
|
-
create_dirs, "create_dirs", default=True
|
|
644
|
-
)
|
|
645
|
-
|
|
646
633
|
# Check if custom component is available
|
|
647
634
|
await _assert_mcp_tools_available(self._client)
|
|
648
635
|
|
|
@@ -650,8 +637,8 @@ class FilesystemTools:
|
|
|
650
637
|
service_data: dict[str, Any] = {
|
|
651
638
|
"path": path,
|
|
652
639
|
"content": content,
|
|
653
|
-
"overwrite":
|
|
654
|
-
"create_dirs":
|
|
640
|
+
"overwrite": overwrite,
|
|
641
|
+
"create_dirs": create_dirs,
|
|
655
642
|
}
|
|
656
643
|
|
|
657
644
|
# Call the custom component service
|
|
@@ -705,7 +692,7 @@ class FilesystemTools:
|
|
|
705
692
|
),
|
|
706
693
|
],
|
|
707
694
|
confirm: Annotated[
|
|
708
|
-
bool
|
|
695
|
+
bool,
|
|
709
696
|
Field(
|
|
710
697
|
default=False,
|
|
711
698
|
description=(
|
|
@@ -747,10 +734,7 @@ class FilesystemTools:
|
|
|
747
734
|
```
|
|
748
735
|
"""
|
|
749
736
|
try:
|
|
750
|
-
|
|
751
|
-
confirm_bool = coerce_bool_param(confirm, "confirm", default=False)
|
|
752
|
-
|
|
753
|
-
if not confirm_bool:
|
|
737
|
+
if not confirm:
|
|
754
738
|
raise_tool_error(
|
|
755
739
|
create_error_response(
|
|
756
740
|
ErrorCode.VALIDATION_INVALID_PARAMETER,
|
|
@@ -26,7 +26,6 @@ from .helpers import (
|
|
|
26
26
|
validate_identifier_not_empty,
|
|
27
27
|
)
|
|
28
28
|
from .util_helpers import (
|
|
29
|
-
coerce_bool_param,
|
|
30
29
|
wait_for_entity_registered,
|
|
31
30
|
wait_for_entity_removed,
|
|
32
31
|
)
|
|
@@ -264,7 +263,7 @@ class GroupTools:
|
|
|
264
263
|
),
|
|
265
264
|
] = None,
|
|
266
265
|
wait: Annotated[
|
|
267
|
-
bool
|
|
266
|
+
bool,
|
|
268
267
|
Field(
|
|
269
268
|
description="Wait for group to be queryable before returning. Default: True. Set to False for bulk operations.",
|
|
270
269
|
default=True,
|
|
@@ -340,10 +339,8 @@ class GroupTools:
|
|
|
340
339
|
and remove_entities is None
|
|
341
340
|
)
|
|
342
341
|
|
|
343
|
-
# Verify entity is queryable after creation/update
|
|
344
|
-
wait_bool = coerce_bool_param(wait, "wait", default=True)
|
|
345
342
|
result: dict[str, Any] = {}
|
|
346
|
-
if
|
|
343
|
+
if wait:
|
|
347
344
|
action_word = "created" if is_create else "updated"
|
|
348
345
|
try:
|
|
349
346
|
registered = await wait_for_entity_registered(
|
|
@@ -403,7 +400,7 @@ class GroupTools:
|
|
|
403
400
|
),
|
|
404
401
|
],
|
|
405
402
|
wait: Annotated[
|
|
406
|
-
bool
|
|
403
|
+
bool,
|
|
407
404
|
Field(
|
|
408
405
|
description="Wait for group to be fully removed before returning. Default: True.",
|
|
409
406
|
default=True,
|
|
@@ -462,10 +459,8 @@ class GroupTools:
|
|
|
462
459
|
|
|
463
460
|
entity_id = f"group.{object_id}"
|
|
464
461
|
|
|
465
|
-
# Verify entity is removed
|
|
466
|
-
wait_bool = coerce_bool_param(wait, "wait", default=True)
|
|
467
462
|
result: dict[str, Any] = {}
|
|
468
|
-
if
|
|
463
|
+
if wait:
|
|
469
464
|
try:
|
|
470
465
|
removed = await wait_for_entity_removed(self._client, entity_id)
|
|
471
466
|
if not removed:
|
|
@@ -26,7 +26,7 @@ from .helpers import (
|
|
|
26
26
|
safe_progress,
|
|
27
27
|
validate_identifier_not_empty,
|
|
28
28
|
)
|
|
29
|
-
from .util_helpers import add_timezone_metadata
|
|
29
|
+
from .util_helpers import add_timezone_metadata
|
|
30
30
|
|
|
31
31
|
logger = logging.getLogger(__name__)
|
|
32
32
|
|
|
@@ -122,23 +122,26 @@ class HacsTools:
|
|
|
122
122
|
),
|
|
123
123
|
] = None,
|
|
124
124
|
installed_only: Annotated[
|
|
125
|
-
bool
|
|
125
|
+
bool,
|
|
126
126
|
Field(
|
|
127
127
|
default=False,
|
|
128
128
|
description="Only return installed repositories (default: False)",
|
|
129
129
|
),
|
|
130
130
|
] = False,
|
|
131
131
|
max_results: Annotated[
|
|
132
|
-
int
|
|
132
|
+
int,
|
|
133
133
|
Field(
|
|
134
134
|
default=10,
|
|
135
|
+
ge=1,
|
|
136
|
+
le=100,
|
|
135
137
|
description="Maximum number of results to return (default: 10, max: 100)",
|
|
136
138
|
),
|
|
137
139
|
] = 10,
|
|
138
140
|
offset: Annotated[
|
|
139
|
-
int
|
|
141
|
+
int,
|
|
140
142
|
Field(
|
|
141
143
|
default=0,
|
|
144
|
+
ge=0,
|
|
142
145
|
description="Number of results to skip for pagination (default: 0)",
|
|
143
146
|
),
|
|
144
147
|
] = 0,
|
|
@@ -169,28 +172,10 @@ class HacsTools:
|
|
|
169
172
|
offset: Number of results to skip for pagination (default: 0)
|
|
170
173
|
"""
|
|
171
174
|
try:
|
|
172
|
-
# Coerce parameters
|
|
173
|
-
installed_only_bool = coerce_bool_param(
|
|
174
|
-
installed_only, "installed_only", default=False
|
|
175
|
-
)
|
|
176
|
-
max_results_int = coerce_int_param(
|
|
177
|
-
max_results,
|
|
178
|
-
"max_results",
|
|
179
|
-
default=10,
|
|
180
|
-
min_value=1,
|
|
181
|
-
max_value=100,
|
|
182
|
-
)
|
|
183
|
-
offset_int = coerce_int_param(
|
|
184
|
-
offset,
|
|
185
|
-
"offset",
|
|
186
|
-
default=0,
|
|
187
|
-
min_value=0,
|
|
188
|
-
)
|
|
189
|
-
|
|
190
175
|
await safe_info(
|
|
191
176
|
ctx,
|
|
192
177
|
f"ha_hacs_search starting: query={query!r} "
|
|
193
|
-
f"category={category} installed_only={
|
|
178
|
+
f"category={category} installed_only={installed_only}",
|
|
194
179
|
)
|
|
195
180
|
await safe_progress(
|
|
196
181
|
ctx,
|
|
@@ -242,9 +227,7 @@ class HacsTools:
|
|
|
242
227
|
total=3,
|
|
243
228
|
message=f"filtering {len(all_repositories)} repositories",
|
|
244
229
|
)
|
|
245
|
-
matches = _filter_and_score_repos(
|
|
246
|
-
all_repositories, query, installed_only_bool
|
|
247
|
-
)
|
|
230
|
+
matches = _filter_and_score_repos(all_repositories, query, installed_only)
|
|
248
231
|
await safe_progress(
|
|
249
232
|
ctx,
|
|
250
233
|
progress=3,
|
|
@@ -252,8 +235,8 @@ class HacsTools:
|
|
|
252
235
|
message=f"matched {len(matches)} repositories",
|
|
253
236
|
)
|
|
254
237
|
|
|
255
|
-
limited_matches = matches[
|
|
256
|
-
has_more = (
|
|
238
|
+
limited_matches = matches[offset : offset + max_results]
|
|
239
|
+
has_more = (offset + len(limited_matches)) < len(matches)
|
|
257
240
|
|
|
258
241
|
return await add_timezone_metadata(
|
|
259
242
|
self._client,
|
|
@@ -261,13 +244,13 @@ class HacsTools:
|
|
|
261
244
|
"success": True,
|
|
262
245
|
"query": query if query.strip() else None,
|
|
263
246
|
"category_filter": category,
|
|
264
|
-
"installed_only":
|
|
247
|
+
"installed_only": installed_only,
|
|
265
248
|
"total_matches": len(matches),
|
|
266
|
-
"offset":
|
|
267
|
-
"limit":
|
|
249
|
+
"offset": offset,
|
|
250
|
+
"limit": max_results,
|
|
268
251
|
"count": len(limited_matches),
|
|
269
252
|
"has_more": has_more,
|
|
270
|
-
"next_offset":
|
|
253
|
+
"next_offset": offset + max_results if has_more else None,
|
|
271
254
|
"results": limited_matches,
|
|
272
255
|
},
|
|
273
256
|
)
|