ha-mcp-dev 7.5.0.dev549__tar.gz → 7.5.0.dev551__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.5.0.dev549/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.5.0.dev551}/PKG-INFO +1 -1
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/pyproject.toml +1 -1
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/backup.py +3 -1
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_addons.py +2 -2
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_config_automations.py +59 -6
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_energy.py +3 -3
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_entities.py +27 -10
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_integrations.py +4 -4
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_search.py +1 -1
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_service.py +6 -6
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_system.py +7 -5
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/LICENSE +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/README.md +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/setup.cfg +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/_version.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/client/rest_client.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/config.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/settings_ui.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/smart_search.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/tests/test_env_manager.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ha-mcp-dev"
|
|
7
|
-
version = "7.5.0.
|
|
7
|
+
version = "7.5.0.dev551"
|
|
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"
|
|
@@ -450,7 +450,9 @@ async def restore_backup(
|
|
|
450
450
|
"status": "Restore initiated - Home Assistant will restart",
|
|
451
451
|
"safety_backup_id": safety_backup_id,
|
|
452
452
|
"restore_database": restore_database,
|
|
453
|
-
"
|
|
453
|
+
"warnings": [
|
|
454
|
+
"Home Assistant is restarting. Connection will be temporarily lost."
|
|
455
|
+
],
|
|
454
456
|
"note": "A safety backup was created before restore. You can restore from it if needed.",
|
|
455
457
|
}
|
|
456
458
|
else:
|
|
@@ -1272,7 +1272,7 @@ def _apply_array_ops(
|
|
|
1272
1272
|
and inspectable
|
|
1273
1273
|
and not any(field in it for it in inspectable)
|
|
1274
1274
|
):
|
|
1275
|
-
entry
|
|
1275
|
+
entry.setdefault("warnings", []).append(
|
|
1276
1276
|
f"field {field!r} is not present on any item — "
|
|
1277
1277
|
"check for a typo in the field name"
|
|
1278
1278
|
)
|
|
@@ -2212,7 +2212,7 @@ def register_addon_tools(mcp: Any, client: HomeAssistantClient, **kwargs: Any) -
|
|
|
2212
2212
|
"submitted_fields": submitted_fields,
|
|
2213
2213
|
}
|
|
2214
2214
|
if ignored_fields:
|
|
2215
|
-
response
|
|
2215
|
+
response.setdefault("warnings", []).append(
|
|
2216
2216
|
f"{len(ignored_fields)} field(s) not in add-on schema were ignored "
|
|
2217
2217
|
f"before write: {ignored_fields}. Use ha_get_addon(slug) to see the "
|
|
2218
2218
|
"declared schema."
|
{ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_config_automations.py
RENAMED
|
@@ -231,6 +231,30 @@ def _normalize_config_for_roundtrip(config: dict[str, Any]) -> dict[str, Any]:
|
|
|
231
231
|
return cast(dict[str, Any], normalized)
|
|
232
232
|
|
|
233
233
|
|
|
234
|
+
def _strip_redundant_identifier_echo(
|
|
235
|
+
result: dict[str, Any],
|
|
236
|
+
*,
|
|
237
|
+
extra_excludes: tuple[str, ...] = (),
|
|
238
|
+
) -> dict[str, Any]:
|
|
239
|
+
"""Strip the redundant ``identifier`` echo from an upsert/delete response.
|
|
240
|
+
|
|
241
|
+
The canonical ``automation_id`` key (resolved entity_id, falling back to
|
|
242
|
+
input identifier or ``unique_id``) makes re-echoing the raw ``identifier``
|
|
243
|
+
redundant noise.
|
|
244
|
+
|
|
245
|
+
``unique_id`` is intentionally retained — it's HA's internal identifier,
|
|
246
|
+
distinct from ``entity_id``/``automation_id``, and callers track it for
|
|
247
|
+
cleanup. Do not extend ``extra_excludes`` to ``"unique_id"``: that
|
|
248
|
+
regression broke E2E ``test_duplicate_automation_prevention`` at 5fe5338.
|
|
249
|
+
|
|
250
|
+
``extra_excludes`` lets a call site drop additional internal keys the
|
|
251
|
+
spread shouldn't surface (e.g. ``"success"`` on the python_transform
|
|
252
|
+
branch, where the caller manages that key directly).
|
|
253
|
+
"""
|
|
254
|
+
excluded = {"identifier", *extra_excludes}
|
|
255
|
+
return {k: v for k, v in result.items() if k not in excluded}
|
|
256
|
+
|
|
257
|
+
|
|
234
258
|
class AutomationConfigTools:
|
|
235
259
|
"""Configuration management tools for Home Assistant automations."""
|
|
236
260
|
|
|
@@ -284,7 +308,9 @@ class AutomationConfigTools:
|
|
|
284
308
|
|
|
285
309
|
The returned `config_hash` is stable across consecutive reads of an unchanged config — `compute_config_hash` documents the underlying contract.
|
|
286
310
|
|
|
287
|
-
The returned `automation_id` is the resolved entity_id (canonical
|
|
311
|
+
The returned `automation_id` is the resolved entity_id (canonical
|
|
312
|
+
form, e.g. `automation.morning_routine`) when the registry lookup
|
|
313
|
+
succeeds, falling back to the input `identifier` otherwise.
|
|
288
314
|
|
|
289
315
|
EXAMPLES:
|
|
290
316
|
- Get automation: ha_config_get_automation("automation.morning_routine")
|
|
@@ -406,6 +432,12 @@ class AutomationConfigTools:
|
|
|
406
432
|
"""
|
|
407
433
|
Create or update a Home Assistant automation.
|
|
408
434
|
|
|
435
|
+
The returned `automation_id` is the resolved entity_id (canonical
|
|
436
|
+
form, e.g. `automation.morning_routine`) when entity registration
|
|
437
|
+
succeeds, falling back to the input `identifier` (update path) or
|
|
438
|
+
the generated `unique_id` from the upsert response (fresh create
|
|
439
|
+
when no identifier was passed).
|
|
440
|
+
|
|
409
441
|
Before reaching for ``ha_config_set_automation``, consider whether a
|
|
410
442
|
dedicated tool fits the use case better:
|
|
411
443
|
|
|
@@ -666,12 +698,15 @@ class AutomationConfigTools:
|
|
|
666
698
|
response: dict[str, Any] = {
|
|
667
699
|
"success": True,
|
|
668
700
|
"action": "python_transform",
|
|
669
|
-
"
|
|
701
|
+
"automation_id": (
|
|
702
|
+
entity_id or identifier or result.get("unique_id")
|
|
703
|
+
),
|
|
670
704
|
"config_hash": new_config_hash,
|
|
671
705
|
"python_expression": python_transform,
|
|
672
706
|
"message": f"Automation {identifier} updated via Python transform",
|
|
673
|
-
|
|
674
|
-
|
|
707
|
+
**_strip_redundant_identifier_echo(
|
|
708
|
+
result, extra_excludes=("success",)
|
|
709
|
+
),
|
|
675
710
|
}
|
|
676
711
|
if bp_warnings:
|
|
677
712
|
response["best_practice_warnings"] = bp_warnings
|
|
@@ -761,9 +796,15 @@ class AutomationConfigTools:
|
|
|
761
796
|
|
|
762
797
|
merge_validation_meta(result, validation_meta)
|
|
763
798
|
|
|
799
|
+
automation_id = entity_id or identifier or result.get("unique_id")
|
|
764
800
|
return {
|
|
765
801
|
"success": True,
|
|
766
|
-
|
|
802
|
+
# automation_id omitted when all three fallbacks are falsy —
|
|
803
|
+
# the create path is unguarded by validate_identifier_not_empty,
|
|
804
|
+
# and surfacing automation_id=None would lie about resolvability.
|
|
805
|
+
# HA's upsert contract makes this branch unreachable in practice.
|
|
806
|
+
**({"automation_id": automation_id} if automation_id else {}),
|
|
807
|
+
**_strip_redundant_identifier_echo(result),
|
|
767
808
|
}
|
|
768
809
|
|
|
769
810
|
except ToolError:
|
|
@@ -1001,6 +1042,11 @@ class AutomationConfigTools:
|
|
|
1001
1042
|
"""
|
|
1002
1043
|
Delete a Home Assistant automation.
|
|
1003
1044
|
|
|
1045
|
+
The returned `automation_id` is the resolved entity_id (canonical
|
|
1046
|
+
form, e.g. `automation.morning_routine`) when the registry lookup
|
|
1047
|
+
succeeded before the delete, falling back to the input
|
|
1048
|
+
`identifier` otherwise.
|
|
1049
|
+
|
|
1004
1050
|
EXAMPLES:
|
|
1005
1051
|
- Delete automation: ha_config_remove_automation("automation.old_automation")
|
|
1006
1052
|
- Delete by unique_id: ha_config_remove_automation("my_unique_id")
|
|
@@ -1040,7 +1086,14 @@ class AutomationConfigTools:
|
|
|
1040
1086
|
f"Deletion confirmed but removal verification failed: {e}"
|
|
1041
1087
|
)
|
|
1042
1088
|
|
|
1043
|
-
return {
|
|
1089
|
+
return {
|
|
1090
|
+
"success": True,
|
|
1091
|
+
"action": "delete",
|
|
1092
|
+
"automation_id": (
|
|
1093
|
+
entity_id_for_wait or identifier or result.get("unique_id")
|
|
1094
|
+
),
|
|
1095
|
+
**_strip_redundant_identifier_echo(result),
|
|
1096
|
+
}
|
|
1044
1097
|
except ToolError:
|
|
1045
1098
|
raise
|
|
1046
1099
|
except Exception as e:
|
|
@@ -673,7 +673,7 @@ class EnergyTools:
|
|
|
673
673
|
}
|
|
674
674
|
if validate_warning is not None:
|
|
675
675
|
response["partial"] = True
|
|
676
|
-
response
|
|
676
|
+
response.setdefault("warnings", []).append(validate_warning)
|
|
677
677
|
return response
|
|
678
678
|
|
|
679
679
|
except ToolError:
|
|
@@ -953,14 +953,14 @@ class EnergyTools:
|
|
|
953
953
|
}
|
|
954
954
|
if post_save_errors:
|
|
955
955
|
response["post_save_validation_errors"] = post_save_errors
|
|
956
|
-
response
|
|
956
|
+
response.setdefault("warnings", []).append(
|
|
957
957
|
f"Save succeeded, but the persisted config has "
|
|
958
958
|
f"{len(post_save_errors)} validation error(s). Review "
|
|
959
959
|
"and re-write if any relate to this change."
|
|
960
960
|
)
|
|
961
961
|
elif post_save_validate_error is not None:
|
|
962
962
|
response["partial"] = True
|
|
963
|
-
response
|
|
963
|
+
response.setdefault("warnings", []).append(
|
|
964
964
|
f"Save succeeded, but post-save energy/validate "
|
|
965
965
|
f"failed: {post_save_validate_error}. The persisted "
|
|
966
966
|
"config has not been re-validated."
|
|
@@ -376,16 +376,27 @@ def register_entity_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
376
376
|
_extract_ws_error(get_result),
|
|
377
377
|
)
|
|
378
378
|
device_rename_result = {
|
|
379
|
-
"
|
|
379
|
+
"warnings": [
|
|
380
|
+
"Entity registry lookup failed — could not determine device. Retry may succeed."
|
|
381
|
+
],
|
|
382
|
+
"lookup_failed": True,
|
|
380
383
|
}
|
|
381
384
|
|
|
382
385
|
device_id = (
|
|
383
386
|
entity_entry.get("device_id") if not device_rename_result else None
|
|
384
387
|
)
|
|
385
388
|
if not device_id:
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
+
# Only fire the "no device" warning when the registry lookup
|
|
390
|
+
# succeeded — otherwise the L378 "lookup failed" warning
|
|
391
|
+
# already carries the more accurate signal, and a second
|
|
392
|
+
# "no associated device" claim would be unverified (we don't
|
|
393
|
+
# actually know what the registry says when the lookup failed).
|
|
394
|
+
if device_rename_result is None:
|
|
395
|
+
device_rename_result = {
|
|
396
|
+
"warnings": [
|
|
397
|
+
"Entity has no associated device — device rename skipped"
|
|
398
|
+
],
|
|
399
|
+
}
|
|
389
400
|
else:
|
|
390
401
|
device_msg: dict[str, Any] = {
|
|
391
402
|
"type": "config/device_registry/update",
|
|
@@ -397,7 +408,9 @@ def register_entity_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
397
408
|
device_rename_result = {"success": True, "device_id": device_id}
|
|
398
409
|
else:
|
|
399
410
|
device_rename_result = {
|
|
400
|
-
"
|
|
411
|
+
"warnings": [
|
|
412
|
+
f"Entity updated but device rename failed: {_extract_ws_error(device_result)}"
|
|
413
|
+
],
|
|
401
414
|
"device_id": device_id,
|
|
402
415
|
}
|
|
403
416
|
|
|
@@ -511,7 +524,7 @@ def register_entity_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
511
524
|
# Include old_entity_id and rename warning when a rename was performed
|
|
512
525
|
if new_entity_id is not None:
|
|
513
526
|
response_data["old_entity_id"] = original_entity_id
|
|
514
|
-
response_data
|
|
527
|
+
response_data.setdefault("warnings", []).append(
|
|
515
528
|
"Remember to update any automations, scripts, or dashboards "
|
|
516
529
|
"that reference the old entity_id"
|
|
517
530
|
)
|
|
@@ -521,10 +534,14 @@ def register_entity_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
521
534
|
|
|
522
535
|
if device_rename_result is not None:
|
|
523
536
|
response_data["device_rename"] = device_rename_result
|
|
524
|
-
#
|
|
525
|
-
#
|
|
526
|
-
|
|
527
|
-
|
|
537
|
+
# Mark partial when a device rename was requested but didn't complete
|
|
538
|
+
# for an operational reason: WS-call failure (device_id present + warnings)
|
|
539
|
+
# or upstream registry lookup failure (lookup_failed marker). Not partial
|
|
540
|
+
# when the entity simply has no device — that's a no-op, not an incomplete
|
|
541
|
+
# operation.
|
|
542
|
+
if device_rename_result.get("warnings") and (
|
|
543
|
+
device_rename_result.get("device_id")
|
|
544
|
+
or device_rename_result.get("lookup_failed")
|
|
528
545
|
):
|
|
529
546
|
response_data["partial"] = True
|
|
530
547
|
|
|
@@ -1237,13 +1237,13 @@ class IntegrationTools:
|
|
|
1237
1237
|
if res is not True
|
|
1238
1238
|
]
|
|
1239
1239
|
if not_removed:
|
|
1240
|
-
response
|
|
1240
|
+
response.setdefault("warnings", []).append(
|
|
1241
1241
|
f"Deletion confirmed but the following entities "
|
|
1242
1242
|
f"are still present after the wait window: "
|
|
1243
1243
|
f"{not_removed}"
|
|
1244
1244
|
)
|
|
1245
1245
|
if warnings:
|
|
1246
|
-
response
|
|
1246
|
+
response.setdefault("warnings", []).extend(warnings)
|
|
1247
1247
|
return response
|
|
1248
1248
|
|
|
1249
1249
|
except ToolError:
|
|
@@ -1388,7 +1388,7 @@ class IntegrationTools:
|
|
|
1388
1388
|
client, entity_id
|
|
1389
1389
|
)
|
|
1390
1390
|
if not removed:
|
|
1391
|
-
response
|
|
1391
|
+
response.setdefault("warnings", []).append(
|
|
1392
1392
|
f"Deletion confirmed but {entity_id} "
|
|
1393
1393
|
"is still present after the wait window."
|
|
1394
1394
|
)
|
|
@@ -1531,7 +1531,7 @@ class IntegrationTools:
|
|
|
1531
1531
|
client, entity_id
|
|
1532
1532
|
)
|
|
1533
1533
|
if not removed:
|
|
1534
|
-
response
|
|
1534
|
+
response.setdefault("warnings", []).append(
|
|
1535
1535
|
f"Deletion confirmed but {entity_id} "
|
|
1536
1536
|
"is still present after the wait window."
|
|
1537
1537
|
)
|
|
@@ -736,7 +736,7 @@ def register_search_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
736
736
|
|
|
737
737
|
# Add warning and partial flag if fallback was used
|
|
738
738
|
if warning:
|
|
739
|
-
result
|
|
739
|
+
result.setdefault("warnings", []).append(warning)
|
|
740
740
|
result["partial"] = True
|
|
741
741
|
|
|
742
742
|
return await add_timezone_metadata(client, result)
|
|
@@ -145,14 +145,14 @@ class ServiceTools:
|
|
|
145
145
|
f"did not respond within the timeout period. The operation is likely "
|
|
146
146
|
f"still running in the background."
|
|
147
147
|
),
|
|
148
|
-
"
|
|
148
|
+
"warnings": [
|
|
149
149
|
"Response timed out. This is normal for long-running services "
|
|
150
150
|
f"like updates or firmware installs. Use ha_get_state('{entity_id}') "
|
|
151
151
|
"to check the current status."
|
|
152
152
|
if entity_id
|
|
153
153
|
else "Response timed out. This is normal for long-running services. "
|
|
154
154
|
"The service was dispatched and may still be executing."
|
|
155
|
-
|
|
155
|
+
],
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
async def _capture_initial_state(self, entity_id: str | None) -> str | None:
|
|
@@ -186,11 +186,11 @@ class ServiceTools:
|
|
|
186
186
|
if new_state:
|
|
187
187
|
response["verified_state"] = new_state.get("state")
|
|
188
188
|
else:
|
|
189
|
-
response
|
|
189
|
+
response.setdefault("warnings", []).append(
|
|
190
190
|
"Service executed but state change could not be verified within timeout."
|
|
191
191
|
)
|
|
192
192
|
except Exception as e:
|
|
193
|
-
response
|
|
193
|
+
response.setdefault("warnings", []).append(
|
|
194
194
|
f"Service executed but state verification failed: {e}"
|
|
195
195
|
)
|
|
196
196
|
|
|
@@ -512,10 +512,10 @@ class ServiceTools:
|
|
|
512
512
|
f"Event {event_type} was dispatched but Home Assistant "
|
|
513
513
|
"did not respond within the timeout period."
|
|
514
514
|
),
|
|
515
|
-
"
|
|
515
|
+
"warnings": [
|
|
516
516
|
"Response timed out. The event was dispatched and may still "
|
|
517
517
|
"have been delivered to subscribers."
|
|
518
|
-
|
|
518
|
+
],
|
|
519
519
|
}
|
|
520
520
|
exception_to_structured_error(
|
|
521
521
|
error,
|
|
@@ -186,10 +186,10 @@ class SystemTools:
|
|
|
186
186
|
"Home Assistant restart initiated. "
|
|
187
187
|
"The system will be unavailable for 1-5 minutes."
|
|
188
188
|
),
|
|
189
|
-
"
|
|
189
|
+
"warnings": [
|
|
190
190
|
"Connection will be lost during restart. "
|
|
191
191
|
"Wait for Home Assistant to become available again."
|
|
192
|
-
|
|
192
|
+
],
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
except ToolError:
|
|
@@ -208,7 +208,7 @@ class SystemTools:
|
|
|
208
208
|
"Home Assistant restart initiated. "
|
|
209
209
|
"Connection was closed as expected during restart."
|
|
210
210
|
),
|
|
211
|
-
"
|
|
211
|
+
"warnings": ["Wait 1-5 minutes for Home Assistant to restart."],
|
|
212
212
|
}
|
|
213
213
|
|
|
214
214
|
exception_to_structured_error(e)
|
|
@@ -299,12 +299,14 @@ class SystemTools:
|
|
|
299
299
|
if "not found" not in error_msg.lower():
|
|
300
300
|
errors.append(f"{reload_target}: {error_msg}")
|
|
301
301
|
|
|
302
|
-
|
|
302
|
+
response: dict[str, Any] = {
|
|
303
303
|
"success": True,
|
|
304
304
|
"message": f"Reloaded {len(results)} components",
|
|
305
305
|
"reloaded": results,
|
|
306
|
-
"warnings": errors if errors else None,
|
|
307
306
|
}
|
|
307
|
+
if errors:
|
|
308
|
+
response["warnings"] = errors
|
|
309
|
+
return response
|
|
308
310
|
|
|
309
311
|
else:
|
|
310
312
|
# Reload specific component
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/AGENTS.md
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/CLAUDE.md
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/LICENSE
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/resources/skills-vendor/README.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/best_practice_checker.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_config_dashboards.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_config_entry_flow.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_config_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_config_scripts.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/tools/tools_voice_assistant.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/transforms/categorized_search.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/transforms/lite_docstrings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp/utils/kill_signal_diagnostics.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp_dev.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev549 → ha_mcp_dev-7.5.0.dev551}/src/ha_mcp_dev.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|