ha-mcp-dev 7.3.0.dev388__tar.gz → 7.3.0.dev389__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.
Files changed (101) hide show
  1. {ha_mcp_dev-7.3.0.dev388/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.3.0.dev389}/PKG-INFO +1 -1
  2. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/pyproject.toml +1 -1
  3. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_areas.py +139 -0
  4. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  5. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/LICENSE +0 -0
  6. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/MANIFEST.in +0 -0
  7. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/README.md +0 -0
  8. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/setup.cfg +0 -0
  9. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/__init__.py +0 -0
  10. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/__main__.py +0 -0
  11. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/_pypi_marker +0 -0
  12. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/auth/__init__.py +0 -0
  13. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/auth/consent_form.py +0 -0
  14. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/auth/provider.py +0 -0
  15. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/client/__init__.py +0 -0
  16. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/client/rest_client.py +0 -0
  17. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/client/websocket_client.py +0 -0
  18. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/client/websocket_listener.py +0 -0
  19. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/config.py +0 -0
  20. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/errors.py +0 -0
  21. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/py.typed +0 -0
  22. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  23. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  24. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  25. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  26. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  27. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  28. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  29. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  30. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  31. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  32. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  33. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  34. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  35. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  36. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  37. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  38. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  39. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  40. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  41. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  42. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/server.py +0 -0
  43. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/smoke_test.py +0 -0
  44. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/__init__.py +0 -0
  45. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/backup.py +0 -0
  46. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  47. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/device_control.py +0 -0
  48. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/enhanced.py +0 -0
  49. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/helpers.py +0 -0
  50. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/registry.py +0 -0
  51. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/smart_search.py +0 -0
  52. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_addons.py +0 -0
  53. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  54. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  55. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_calendar.py +0 -0
  56. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_camera.py +0 -0
  57. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_categories.py +0 -0
  58. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  59. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  60. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
  61. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  62. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  63. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_entities.py +0 -0
  64. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  65. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_groups.py +0 -0
  66. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_hacs.py +0 -0
  67. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_history.py +0 -0
  68. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_integrations.py +0 -0
  69. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_labels.py +0 -0
  70. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
  71. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_registry.py +0 -0
  72. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_resources.py +0 -0
  73. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_search.py +0 -0
  74. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_service.py +0 -0
  75. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_services.py +0 -0
  76. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_system.py +0 -0
  77. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_todo.py +0 -0
  78. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_traces.py +0 -0
  79. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_updates.py +0 -0
  80. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_utility.py +0 -0
  81. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  82. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  83. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/tools_zones.py +0 -0
  84. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/tools/util_helpers.py +0 -0
  85. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/transforms/__init__.py +0 -0
  86. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/transforms/categorized_search.py +0 -0
  87. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/utils/__init__.py +0 -0
  88. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/utils/config_hash.py +0 -0
  89. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/utils/domain_handlers.py +0 -0
  90. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  91. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/utils/operation_manager.py +0 -0
  92. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/utils/python_sandbox.py +0 -0
  93. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp/utils/usage_logger.py +0 -0
  94. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
  95. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  96. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  97. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  98. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  99. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/tests/__init__.py +0 -0
  100. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/tests/test_constants.py +0 -0
  101. {ha_mcp_dev-7.3.0.dev388 → ha_mcp_dev-7.3.0.dev389}/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.3.0.dev388
3
+ Version: 7.3.0.dev389
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ha-mcp-dev"
7
- version = "7.3.0.dev388"
7
+ version = "7.3.0.dev389"
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"
@@ -387,6 +387,145 @@ class AreaTools:
387
387
  "Verify WebSocket connection is active",
388
388
  ])
389
389
 
390
+ @tool(
391
+ name="ha_list_floors_areas",
392
+ tags={"Areas & Floors"},
393
+ annotations={"idempotentHint": True, "readOnlyHint": True, "title": "List Floors and Areas"},
394
+ )
395
+ @log_tool_usage
396
+ async def ha_list_floors_areas(self) -> dict[str, Any]:
397
+ """
398
+ List floors sorted by level ascending, each with their assigned areas nested, plus areas without a floor.
399
+
400
+ Do not use for flat listings — ha_config_list_areas and ha_config_list_floors cover those.
401
+
402
+ Use for location-based reasoning where floor-to-area relationships matter, such as "which rooms are on the ground floor" or operations scoped to a level.
403
+
404
+ Floors with level=None sort alongside level 0 (ground floor). Areas without a floor assignment appear in unassigned_areas; areas whose floor_id points to a non-existent floor appear in orphaned_areas — a topology snapshot may diverge from individual list calls if the registries change between reads.
405
+ """
406
+ progress: dict[str, Any] = {
407
+ "operation": "list_floors_areas",
408
+ "phase": "start",
409
+ }
410
+ try:
411
+ areas_result = await self._client.send_websocket_message(
412
+ {"type": "config/area_registry/list"}
413
+ )
414
+ progress["phase"] = "areas_fetched"
415
+ floors_result = await self._client.send_websocket_message(
416
+ {"type": "config/floor_registry/list"}
417
+ )
418
+ progress["phase"] = "floors_fetched"
419
+
420
+ # A response with success=True but no "result" key is malformed —
421
+ # treat it as a service call failure rather than silently returning
422
+ # floor_count=0, area_count=0 on a populated instance.
423
+ areas_ok = areas_result.get("success") and "result" in areas_result
424
+ floors_ok = floors_result.get("success") and "result" in floors_result
425
+ if not (areas_ok and floors_ok):
426
+ raise_tool_error(create_error_response(
427
+ ErrorCode.SERVICE_CALL_FAILED,
428
+ "Failed to retrieve area or floor registry",
429
+ context={
430
+ "areas_success": areas_result.get("success"),
431
+ "floors_success": floors_result.get("success"),
432
+ "areas_response_keys": sorted(areas_result.keys()),
433
+ "floors_response_keys": sorted(floors_result.keys()),
434
+ },
435
+ suggestions=[
436
+ "Check Home Assistant connection",
437
+ "Verify WebSocket connection is active",
438
+ ],
439
+ ))
440
+
441
+ areas = areas_result["result"]
442
+ floors = floors_result["result"]
443
+
444
+ # Partition areas into three disjoint sets:
445
+ # - nested: floor_id present AND points to a known floor
446
+ # - orphaned: floor_id present BUT points to a non-existent floor
447
+ # (race between the two sequential reads, or manual
448
+ # .storage inconsistency)
449
+ # - unassigned: no floor_id at all
450
+ # Orphaned is surfaced as a separate key so the LLM can diagnose
451
+ # registry drift without introspecting individual area fields.
452
+ # Use `is None` rather than falsy-check so that a floor_id of ""
453
+ # (valid but unusual) is treated as orphaned if it does not resolve,
454
+ # not as unassigned.
455
+ valid_floor_ids = {
456
+ f.get("floor_id") for f in floors if f.get("floor_id") is not None
457
+ }
458
+ floor_map: dict[str, list[dict[str, Any]]] = {}
459
+ unassigned_areas: list[dict[str, Any]] = []
460
+ orphaned_areas: list[dict[str, Any]] = []
461
+ for area in areas:
462
+ fid = area.get("floor_id")
463
+ if fid is None:
464
+ unassigned_areas.append(area)
465
+ elif fid in valid_floor_ids:
466
+ floor_map.setdefault(fid, []).append(area)
467
+ else:
468
+ orphaned_areas.append(area)
469
+ progress["phase"] = "partitioned"
470
+
471
+ # Build nested hierarchy, preserving all floor-registry fields for
472
+ # forward compatibility with future HA Core additions
473
+ topology = [
474
+ {**floor, "areas": floor_map.get(floor.get("floor_id"), [])}
475
+ for floor in floors
476
+ ]
477
+
478
+ # Sort by level ascending; coerce defensively so a malformed
479
+ # string `level` cannot raise TypeError mid-sort and get
480
+ # flattened by the broad `except Exception` below.
481
+ def _floor_sort_key(floor: dict[str, Any]) -> int:
482
+ raw = floor.get("level")
483
+ if raw is None:
484
+ return 0
485
+ try:
486
+ return int(raw)
487
+ except (TypeError, ValueError):
488
+ logger.warning(
489
+ f"Floor {floor.get('floor_id')!r} has non-numeric "
490
+ f"level {raw!r}; treating as 0 for sort"
491
+ )
492
+ return 0
493
+
494
+ topology.sort(key=_floor_sort_key)
495
+ progress["phase"] = "sorted"
496
+
497
+ return {
498
+ "success": True,
499
+ "floor_count": len(topology),
500
+ "area_count": len(areas),
501
+ "unassigned_count": len(unassigned_areas),
502
+ "orphaned_count": len(orphaned_areas),
503
+ "floors": topology,
504
+ "unassigned_areas": unassigned_areas,
505
+ "orphaned_areas": orphaned_areas,
506
+ "message": (
507
+ f"Found {len(topology)} floor(s), {len(areas)} area(s), "
508
+ f"{len(unassigned_areas)} unassigned, "
509
+ f"{len(orphaned_areas)} orphaned"
510
+ ),
511
+ }
512
+
513
+ except ToolError:
514
+ raise
515
+ except Exception as e:
516
+ logger.error(
517
+ f"Error listing floors and areas in phase {progress['phase']!r}: {e} "
518
+ f"(progress={progress})"
519
+ )
520
+ exception_to_structured_error(
521
+ e,
522
+ context=progress,
523
+ suggestions=[
524
+ "Check Home Assistant connection",
525
+ "Verify WebSocket connection is active",
526
+ ],
527
+ )
528
+
390
529
  @tool(
391
530
  name="ha_config_set_floor",
392
531
  tags={"Areas & Floors"},
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.3.0.dev388
3
+ Version: 7.3.0.dev389
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