ha-mcp-dev 7.10.0.dev775__tar.gz → 7.10.0.dev777__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 (160) hide show
  1. {ha_mcp_dev-7.10.0.dev775/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.10.0.dev777}/PKG-INFO +1 -1
  2. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/pyproject.toml +1 -1
  3. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/client/websocket_client.py +72 -13
  4. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/README.md +1 -0
  5. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +15 -5
  6. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +79 -28
  7. ha_mcp_dev-7.10.0.dev777/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +271 -0
  8. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +119 -94
  9. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +14 -0
  10. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/smart_search/_entities.py +80 -26
  11. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  12. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp_dev.egg-info/SOURCES.txt +1 -0
  13. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/LICENSE +0 -0
  14. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/MANIFEST.in +0 -0
  15. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/README.md +0 -0
  16. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/setup.cfg +0 -0
  17. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/__init__.py +0 -0
  18. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/__main__.py +0 -0
  19. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/_pypi_marker +0 -0
  20. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/_version.py +0 -0
  21. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/auth/__init__.py +0 -0
  22. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/auth/consent_form.py +0 -0
  23. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/auth/provider.py +0 -0
  24. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/backup_manager.py +0 -0
  25. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/client/__init__.py +0 -0
  26. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/client/rest_client.py +0 -0
  27. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/client/supervisor_client.py +0 -0
  28. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/client/websocket_listener.py +0 -0
  29. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/config.py +0 -0
  30. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
  31. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
  32. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
  33. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/errors.py +0 -0
  34. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/policy/__init__.py +0 -0
  35. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/policy/approval_queue.py +0 -0
  36. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/policy/evaluator.py +0 -0
  37. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/policy/handlers.py +0 -0
  38. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/policy/middleware.py +0 -0
  39. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/policy/model.py +0 -0
  40. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/policy/persistence.py +0 -0
  41. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/policy/value_sources.py +0 -0
  42. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/py.typed +0 -0
  43. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/read_only.py +0 -0
  44. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  45. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  46. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  47. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  48. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  49. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  50. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  51. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  52. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  53. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  54. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
  55. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  56. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  57. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  58. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  59. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  60. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
  61. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  62. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  63. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/server.py +0 -0
  64. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/settings_ui/__init__.py +0 -0
  65. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/settings_ui/settings.css +0 -0
  66. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/settings_ui/settings.html +0 -0
  67. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/settings_ui/settings.js +0 -0
  68. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/smoke_test.py +0 -0
  69. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  70. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/__init__.py +0 -0
  71. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/auto_backup.py +0 -0
  72. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/backup.py +0 -0
  73. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  74. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/config_entry_flow.py +0 -0
  75. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/device_control.py +0 -0
  76. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/enhanced.py +0 -0
  77. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/helpers.py +0 -0
  78. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/radio/__init__.py +0 -0
  79. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/radio/base.py +0 -0
  80. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/radio/matter.py +0 -0
  81. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/radio/thread.py +0 -0
  82. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/radio/zigbee.py +0 -0
  83. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/radio/zwave.py +0 -0
  84. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/reference_validator.py +0 -0
  85. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/registry.py +0 -0
  86. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  87. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  88. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  89. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
  90. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
  91. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
  92. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
  93. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  94. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
  95. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_addons.py +0 -0
  96. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_areas.py +0 -0
  97. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  98. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  99. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_calendar.py +0 -0
  100. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_camera.py +0 -0
  101. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_categories.py +0 -0
  102. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_code.py +0 -0
  103. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  104. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  105. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  106. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  107. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  108. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
  109. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_energy.py +0 -0
  110. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_entities.py +0 -0
  111. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  112. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_groups.py +0 -0
  113. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_hacs.py +0 -0
  114. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_history.py +0 -0
  115. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_integrations.py +0 -0
  116. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_labels.py +0 -0
  117. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
  118. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_radio.py +0 -0
  119. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_registry.py +0 -0
  120. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_resources.py +0 -0
  121. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_search.py +0 -0
  122. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_service.py +0 -0
  123. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_services.py +0 -0
  124. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_system.py +0 -0
  125. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_themes.py +0 -0
  126. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_todo.py +0 -0
  127. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_traces.py +0 -0
  128. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_updates.py +0 -0
  129. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_utility.py +0 -0
  130. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  131. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  132. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_zones.py +0 -0
  133. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/util_helpers.py +0 -0
  134. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/validation_middleware.py +0 -0
  135. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/transforms/__init__.py +0 -0
  136. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/transforms/categorized_search.py +0 -0
  137. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  138. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/transport_security.py +0 -0
  139. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/update_check.py +0 -0
  140. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/utils/__init__.py +0 -0
  141. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/utils/config_hash.py +0 -0
  142. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/utils/data_paths.py +0 -0
  143. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/utils/domain_handlers.py +0 -0
  144. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  145. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  146. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/utils/operation_manager.py +0 -0
  147. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/utils/python_sandbox.py +0 -0
  148. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/utils/skill_loader.py +0 -0
  149. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/utils/usage_logger.py +0 -0
  150. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/visibility/__init__.py +0 -0
  151. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/visibility/model.py +0 -0
  152. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/visibility/persistence.py +0 -0
  153. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/visibility/resolver.py +0 -0
  154. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  155. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  156. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  157. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  158. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/tests/__init__.py +0 -0
  159. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/tests/test_constants.py +0 -0
  160. {ha_mcp_dev-7.10.0.dev775 → ha_mcp_dev-7.10.0.dev777}/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.10.0.dev775
3
+ Version: 7.10.0.dev777
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.10.0.dev775"
7
+ version = "7.10.0.dev777"
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
@@ -30,6 +30,15 @@ from .rest_client import (
30
30
 
31
31
  logger = logging.getLogger(__name__)
32
32
 
33
+ # Matches the Supervisor's own Core-connection receive limit
34
+ # (MAX_MESSAGE_SIZE_FROM_CORE in home-assistant/supervisor, see supervisor
35
+ # issue #4392). On the add-on path frames above that limit die at the
36
+ # Supervisor proxy anyway, so a larger client-side cap adds nothing there.
37
+ # Registry list responses scale with entity count and arrive as ONE frame
38
+ # (the HA WebSocket API has no pagination); a ~6.4k-entity instance
39
+ # overflowed the previous 20MB cap (#1721).
40
+ MAX_WS_MESSAGE_BYTES = 64 * 1024 * 1024
41
+
33
42
 
34
43
  class WebSocketConnectionState:
35
44
  """Encapsulates mutable state used by the WebSocket client."""
@@ -109,20 +118,43 @@ class WebSocketConnectionState:
109
118
  """Retrieve and remove an authentication message if present."""
110
119
  return self._auth_messages.pop(message_type, None)
111
120
 
112
- def reset_connection(self) -> None:
113
- """Reset connection-specific state while preserving handlers."""
121
+ def reset_connection(self, close_reason: str | None = None) -> None:
122
+ """Reset connection-specific state while preserving handlers.
123
+
124
+ Args:
125
+ close_reason: Human-readable description of why the connection
126
+ went away (e.g. a close code/reason pair), included in the
127
+ error surfaced to any request still awaiting a response.
128
+ """
114
129
  self.connected = False
115
130
  self.authenticated = False
116
131
  self._message_id = 0
117
132
 
133
+ # ``future.cancel()`` makes awaiters see ``asyncio.CancelledError``,
134
+ # a BaseException that skips every ``except Exception`` handler in
135
+ # the tool layer and reaches the MCP SDK, which treats it as a
136
+ # client-initiated cancellation, suppresses the response entirely,
137
+ # and leaves the MCP client hanging until its own timeout (#1721).
138
+ # ``set_exception`` with a normal exception propagates through
139
+ # ``except Exception`` as expected instead.
140
+ message = (
141
+ "WebSocket connection to Home Assistant closed while waiting for a response"
142
+ )
143
+ if close_reason:
144
+ message = f"{message} ({close_reason})"
145
+
118
146
  for future in self._pending_requests.values():
119
147
  if not future.done():
120
- future.cancel()
148
+ # A fresh instance per future: sharing one exception object
149
+ # across multiple ``set_exception`` calls means the second
150
+ # raise attaches a traceback to an exception already
151
+ # associated with another future's stack.
152
+ future.set_exception(HomeAssistantConnectionError(message))
121
153
  self._pending_requests.clear()
122
154
 
123
155
  for future in self._event_responses.values():
124
156
  if not future.done():
125
- future.cancel()
157
+ future.set_exception(HomeAssistantConnectionError(message))
126
158
  self._event_responses.clear()
127
159
 
128
160
  # Drop any subscription queues — readers wake on the close signal
@@ -144,9 +176,9 @@ class WebSocketConnectionState:
144
176
  """Mark the socket as authenticated and ready for commands."""
145
177
  self.authenticated = True
146
178
 
147
- def mark_disconnected(self) -> None:
179
+ def mark_disconnected(self, close_reason: str | None = None) -> None:
148
180
  """Reset connection state when the socket is closed."""
149
- self.reset_connection()
181
+ self.reset_connection(close_reason)
150
182
 
151
183
  @property
152
184
  def is_ready(self) -> bool:
@@ -293,9 +325,7 @@ class HomeAssistantWebSocketClient:
293
325
  ping_timeout=10,
294
326
  additional_headers={"Authorization": f"Bearer {self.token}"},
295
327
  ssl=ssl_ctx,
296
- # Increase max message size to 20MB for large responses
297
- # (e.g., HACS repository list can be 2MB+)
298
- max_size=20 * 1024 * 1024,
328
+ max_size=MAX_WS_MESSAGE_BYTES,
299
329
  )
300
330
  self._state.mark_connected()
301
331
 
@@ -390,6 +420,11 @@ class HomeAssistantWebSocketClient:
390
420
  """Background task to handle incoming WebSocket messages."""
391
421
  if not self.websocket:
392
422
  raise Exception("WebSocket not connected")
423
+ # None means a clean exit (the async-for loop simply ended); the
424
+ # except blocks below fill this in so pending futures — and the
425
+ # log — carry *why* the connection went away instead of a bare
426
+ # "WebSocket connection closed" that gave no lead on #1721.
427
+ close_reason: str | None = None
393
428
  try:
394
429
  async for message in self.websocket:
395
430
  try:
@@ -400,12 +435,30 @@ class HomeAssistantWebSocketClient:
400
435
  logger.error(f"Invalid JSON received: {e}")
401
436
  except Exception as e:
402
437
  logger.error(f"Error processing message: {e}")
403
- except websockets.exceptions.ConnectionClosed:
404
- logger.info("WebSocket connection closed")
438
+ except websockets.exceptions.ConnectionClosed as e:
439
+ # Prefer the frame we received (the peer closed on us); fall
440
+ # back to the frame we sent (we failed the connection
441
+ # ourselves, e.g. an over-max_size frame produces a sent
442
+ # Close(1009, ...) with no frame received at all).
443
+ close = e.rcvd if e.rcvd is not None else e.sent
444
+ if close is not None:
445
+ verb = "received" if e.rcvd is not None else "sent"
446
+ close_reason = f"{verb} close code {close.code}"
447
+ if close.reason:
448
+ close_reason = f"{close_reason} ({close.reason})"
449
+ else:
450
+ close_reason = "connection dropped without a close frame"
451
+
452
+ log_message = f"WebSocket connection closed ({close_reason})"
453
+ if close is None or close.code not in (1000, 1001):
454
+ logger.warning(log_message)
455
+ else:
456
+ logger.info(log_message)
405
457
  except Exception as e:
458
+ close_reason = str(e)
406
459
  logger.error(f"WebSocket message handler error: {e}")
407
460
  finally:
408
- self._state.mark_disconnected()
461
+ self._state.mark_disconnected(close_reason)
409
462
 
410
463
  async def _process_message(self, data: dict[str, Any]) -> None:
411
464
  """Process incoming WebSocket message."""
@@ -649,9 +702,15 @@ class HomeAssistantWebSocketClient:
649
702
  result_response = await asyncio.wait_for(
650
703
  result_future, timeout=wait_timeout
651
704
  )
652
- except TimeoutError:
705
+ except BaseException:
653
706
  self.cancel_pending_response(message_id)
654
707
  self.cancel_event_response(message_id)
708
+ # A connection drop fails BOTH futures via reset_connection;
709
+ # only result_future gets awaited on this path, so retrieve
710
+ # event_future's exception too or asyncio logs an ERROR-level
711
+ # "Future exception was never retrieved" when it is GC'd.
712
+ if event_future.done() and not event_future.cancelled():
713
+ event_future.exception()
655
714
  raise
656
715
 
657
716
  if not result_response.get("success"):
@@ -63,6 +63,7 @@ The `home-assistant-best-practices` skill includes:
63
63
  | `references/domain-docs.md` | Integration and domain documentation for service calls, entity attributes |
64
64
  | `references/examples.yaml` | Compound examples combining multiple best practices |
65
65
  | `references/appdaemon.md` | AppDaemon apps: when to use vs. native HA, app structure, service calls, scheduling, error handling, safe refactoring impact |
66
+ | `references/blueprint-guide.md` | Blueprint authoring: metadata & `source_url`, inputs & selectors, `target` vs `entity`, defaults, `!input` templating, versioning |
66
67
 
67
68
  ## Contributing
68
69
 
@@ -12,6 +12,7 @@ description: >
12
12
  - Configuring dashboard cards or selecting helpers
13
13
  - Looking up card types or domain docs
14
14
  - Writing or reviewing AppDaemon apps
15
+ - Authoring or editing reusable Blueprints
15
16
 
16
17
  SYMPTOMS:
17
18
  - Agent uses Jinja2 templates where native options exist
@@ -21,8 +22,9 @@ description: >
21
22
  - Agent hard-codes values or uses raw sensor over helper
22
23
  - Agent edits .storage, writes YAML, or generates YAML snippets
23
24
  - Agent tells user to edit configuration.yaml for UI integrations
25
+ - Agent hardcodes entities in a Blueprint or uses free-text input over a selector
24
26
  metadata:
25
- version: 12
27
+ version: 15
26
28
  ---
27
29
 
28
30
  # Home Assistant Best Practices
@@ -39,10 +41,11 @@ If your change affects entity IDs or cross-component references — renaming ent
39
41
 
40
42
  Steps 1-5 below apply to new config or pattern evaluation.
41
43
 
42
- ### 1. Check for native condition/trigger
43
- Before writing any template, check `references/automation-patterns.md` for native alternatives.
44
+ ### 1. Check for a purpose-specific, then generic native, trigger/condition
45
+ Since 2026.7 the default building blocks are purpose-specific triggers/conditions — `<domain>.<name>` keys (motion detected, battery low, door opened) with area/floor/label targets. Check for one that matches the intent first, then a generic native trigger/condition, and only then a template. See `references/automation-patterns.md#purpose-specific-triggers--conditions-default-since-20267`.
44
46
 
45
47
  **Common substitutions:**
48
+ - List of individual sensor entities in a trigger → one purpose-specific trigger with an area/floor/label `target:`
46
49
  - `{{ states('x') | float > 25 }}` → `numeric_state` condition with `above: 25`
47
50
  - `{{ is_state('x', 'on') and is_state('y', 'on') }}` → `condition: and` with state conditions
48
51
  - `{{ now().hour >= 9 }}` → `condition: time` with `after: "09:00:00"`
@@ -109,10 +112,16 @@ See `references/device-control.md#zigbee-buttonremote-patterns`.
109
112
  | `vacuum.send_command` with vendor room IDs | `vacuum.clean_area` with HA `area_id` (if segments are mapped) | Uses native HA areas, works across integrations — but requires segment-to-area mapping in entity settings first | `references/device-control.md#vacuum-control` |
110
113
  | Using `color_temp` (mireds) in light service calls | Use `color_temp_kelvin` | The `color_temp` parameter was removed in 2026.3; only Kelvin is supported | `references/device-control.md#lights` |
111
114
  | Person/Device Tracker `entered_home`/`left_home` device triggers or `is_home`/`is_not_home` conditions | `state` trigger `to: home` / `to: not_home`, or `state` condition | These were removed in 2026.5 — state triggers and conditions are the correct replacements | `references/automation-patterns.md#presence-and-person-triggers-and-conditions-removed-in-20265` |
115
+ | Entity list in a trigger where an area/floor/label target fits | Purpose-specific trigger with `target: {area_id: ...}` | Automation follows area membership as devices change — no stale entity lists | `references/automation-patterns.md#purpose-specific-triggers--conditions-default-since-20267` |
116
+ | Old purpose-specific keys (`battery.low`, `vacuum.docked`, `timer.time_remaining`, ...) or trigger `behavior: any`/`last` | Renamed 2026.7 keys (`battery.became_low`, ...) and `behavior: each`/`all` | Old keys no longer load; old behavior values raise a repair issue and face removal | `references/automation-patterns.md#purpose-specific-triggers--conditions-default-since-20267` |
112
117
  | Registering callbacks or calling `self.turn_on()`/`self.get_state()` in `__init__()` | Register everything in `initialize()` | Plugin connection not established during `__init__` — calls fail silently | `references/appdaemon.md#app-structure-and-lifecycle` |
113
118
  | Calling `run_in` on repeated triggers without cancelling the previous handle | `cancel_timer(self._off_handle)` before each new `run_in` | Every trigger stacks an independent timer — devices toggle unpredictably | `references/appdaemon.md#scheduling-and-timers` |
114
119
  | Storing persistent state in instance variables | Use HA `input_number`, `input_boolean`, or `input_text` helpers | Instance variables reset on app reload or daemon restart | `references/appdaemon.md#state-management-and-inter-app-communication` |
115
120
  | Hardcoding entity IDs inside the class body | Pass entity IDs via `self.args` in `apps.yaml` | Hardcoded IDs prevent reuse and require code edits per installation | `references/appdaemon.md#appsyaml-configuration` |
121
+ | Hardcoding entity IDs in a Blueprint body | Expose them as `!input` with a selector | Hardcoding defeats a blueprint's purpose — it can't be reused | `references/blueprint-guide.md#inputs-and-selectors` |
122
+ | Free-text input for an entity/device in a Blueprint | Typed `entity`/`target`/`device` selector | Text lets typos through and fails silently; selectors validate the choice | `references/blueprint-guide.md#inputs-and-selectors` |
123
+ | `!input` used directly inside a template | Bind it to a `variables:` entry, use the variable | `!input` is a YAML tag, not a template value — the template errors or ignores it | `references/blueprint-guide.md#referencing-inputs-input-and-templating` |
124
+ | Publishing a Blueprint without `source_url` | Set `source_url` to the file's canonical URL | Without it users can't re-import updates and sharing is awkward | `references/blueprint-guide.md#blueprint-metadata` |
116
125
 
117
126
  ---
118
127
 
@@ -123,7 +132,7 @@ Read these when you need detailed information:
123
132
  | File | When to read | Key sections |
124
133
  |------|--------------|--------------|
125
134
  | `references/safe-refactoring.md` | Renaming entities, replacing helpers, restructuring automations, or any modification to existing config | `#universal-workflow`, `#entity-renames`, `#helper-replacements`, `#trigger-restructuring`, `#config-entry-data--blind-spots-for-entity-registry-renames`, `#storage-mode-dashboards-storagelovelace` |
126
- | `references/automation-patterns.md` | Writing triggers, conditions, waits, variables, or choosing automation modes; capturing action responses; documenting/annotating steps; disabling automations | `#native-conditions`, `#trigger-types`, `#wait-actions`, `#automation-modes`, `#continue-on-error`, `#stopping-a-sequence`, `#variables`, `#capturing-action-responses`, `#repeat-actions`, `#ifthen-vs-choose`, `#parallel-actions`, `#trigger-ids`, `#documenting-automations--scripts`, `#disabling-automations` |
135
+ | `references/automation-patterns.md` | Writing triggers, conditions, waits, variables, or choosing automation modes; capturing action responses; documenting/annotating steps; disabling automations | `#purpose-specific-triggers--conditions-default-since-20267`, `#native-conditions`, `#trigger-types`, `#wait-actions`, `#automation-modes`, `#continue-on-error`, `#stopping-a-sequence`, `#variables`, `#capturing-action-responses`, `#repeat-actions`, `#ifthen-vs-choose`, `#parallel-actions`, `#trigger-ids`, `#documenting-automations--scripts`, `#disabling-automations` |
127
136
  | `references/helper-selection.md` | Deciding whether to use a built-in helper vs template sensor | `#how-helpers-are-created`, `#menu-based-helpers`, `#numeric-aggregation`, `#rate-and-change`, `#time-based-tracking`, `#counting-and-timing`, `#scheduling`, `#entity-grouping`, `#probabilistic-inference`, `#data-smoothing`, `#random-values`, `#climate-control`, `#domain-conversion`, `#template-helpers`, `#decision-matrix` |
128
137
  | `references/template-guidelines.md` | Confirming templates ARE appropriate for a use case | `#when-templates-are-appropriate`, `#when-to-avoid-templates`, `#template-sensor-best-practices`, `#common-patterns`, `#error-handling` |
129
138
  | `references/yaml-only-integrations.md` | Creating or editing YAML-only integrations that have no config flow (e.g. `command_line`, platform-based `mqtt`, `rest`) | `#yaml-only-integration-types`, `#post-edit-actions` |
@@ -131,6 +140,7 @@ Read these when you need detailed information:
131
140
  | `references/scenes.md` | Authoring or activating scenes; snapshot/restore patterns; snapshot-vs-script distinction | `#scene-config-shape`, `#activating-a-scene`, `#snapshot--restore-scenecreate`, `#apply-states-without-storing-sceneapply` |
132
141
  | `references/dashboard-guide.md` | Designing or modifying Lovelace dashboards — layout, view types, strategies, sections, cards, badges, CSS styling, HACS | `#dashboard-structure`, `#view-types`, `#dashboard-strategies`, `#built-in-cards`, `#features`, `#badges`, `#custom-cards`, `#css-styling`, `#common-pitfalls` |
133
142
  | `references/dashboard-cards.md` | Looking up available card types or fetching card-specific documentation | — |
134
- | `references/domain-docs.md` | Looking up integration or domain documentation for service calls, entity attributes, or configuration | |
143
+ | `references/domain-docs.md` | Looking up integration/domain documentation, or the dedicated doc page for a specific trigger, condition, or action | `#fetching-trigger-condition-and-action-docs` |
135
144
  | `references/examples.yaml` | Need compound examples combining multiple best practices | — |
136
145
  | `references/appdaemon.md` | AppDaemon apps: when to use vs. native HA, app structure, service calls, scheduling, error handling, safe refactoring impact | — |
146
+ | `references/blueprint-guide.md` | Authoring reusable blueprints: metadata & `source_url`, inputs & selectors, `target` vs `entity`, defaults, input sections, `!input` templating, versioning | `#when-to-author-a-blueprint`, `#blueprint-metadata`, `#inputs-and-selectors`, `#target-selector-vs-entity-selector`, `#defaults`, `#input-sections`, `#referencing-inputs-input-and-templating`, `#versioning-and-updates`, `#common-pitfalls` |
@@ -229,15 +229,58 @@ id: motion_on # a single id, or a list (OR semantics):
229
229
 
230
230
  ## Trigger Types
231
231
 
232
- ### Purpose-Specific Triggers (2026.2+)
232
+ ### Purpose-Specific Triggers & Conditions (default since 2026.7)
233
233
 
234
- The HA visual automation editor offers **purpose-specific triggers and conditions** organized by real-world concepts (door opened, motion detected, temperature threshold, battery low, etc.) rather than by technical entity domain. These work across entity types e.g., "when a door opens" fires whether the door is a contact sensor or a motorized cover.
234
+ HA organizes triggers and conditions by real-world intent (motion detected, door opened, battery low, temperature crossed threshold) rather than by technical entity domain. Introduced in 2026.2 and expanded each release, this family **left Labs and became the default automation-building experience in 2026.7**. It is not UI-only sugar: these are first-class config — `trigger: <domain>.<name>` / `condition: <domain>.<name>` with `target:` and `options:` blocks — and round-trip through the config API like any other automation config. (The step-level `note:` field is separate additive syntax see [Documenting Automations & Scripts](#documenting-automations--scripts).)
235
235
 
236
- **Key points:**
237
- - **Most** are a UI-editor convenience — under the hood they generate standard `state`, `numeric_state`, etc., so the YAML output uses the same triggers documented below
238
- - **Exceptions (2026.6, Labs):** the native zone trigger/condition family serializes as new YAML (see [Native Zone Triggers & Conditions](#native-zone-triggers--conditions-20266-labs)), and the step-level `note:` field is new additive syntax (see [Documenting Automations & Scripts](#documenting-automations--scripts)).
239
- - Available concepts: door/window/gate open/close, motion/occupancy detection, temperature/humidity/illuminance thresholds, power consumption, battery status, air quality, climate, and more
240
- - When creating automations via the API, use the standard YAML trigger types below (plus the zone family above where you want the native any-zone primitives)
236
+ ```yaml
237
+ triggers:
238
+ - trigger: motion.detected
239
+ target:
240
+ area_id: living_room # entity_id / device_id / area_id / floor_id / label_id
241
+ options:
242
+ behavior: each # multi-target semantics: each (default) / first / all
243
+ for: "00:00:30" # optional dwell time
244
+ conditions:
245
+ - condition: battery.is_low
246
+ target:
247
+ label_id: critical_devices
248
+ options:
249
+ behavior: any # conditions combine with any (default) / all
250
+ ```
251
+
252
+ **Prefer these over the generic triggers below when one matches the intent:**
253
+
254
+ - **Targets, not entity lists.** Target an area/floor/label ("motion in the living room") and the automation follows area membership as sensors are added, swapped, or removed — no entity list to maintain. This also replaces most remaining legitimate uses of device triggers.
255
+ - **No `unavailable`/`unknown` traps.** Each block handles unavailable states and event-entity re-fire quirks internally.
256
+ - **Cross-entity-type.** "Door opened" fires whether the door is a contact sensor or a motorized cover.
257
+ - **Integration-extensible.** Integrations register their own (e.g. `zwave_js.*`), so the catalog grows over time.
258
+
259
+ **Multi-target `behavior` enums differ between triggers and conditions:**
260
+
261
+ - Triggers: `each` (default) / `first` / `all`. The pre-2026.7 values `any` and `last` were renamed to `each` and `all` — they still work but raise a repair issue and face removal. Never emit them (some official doc pages still show the old values mid-migration).
262
+ - Conditions: `any` (default) / `all` — unchanged.
263
+
264
+ **Keys renamed in 2026.7 (trigger keys unless marked *(condition)*) — old keys no longer work:**
265
+
266
+ | Old | New |
267
+ |-----|-----|
268
+ | `battery.low` | `battery.became_low` |
269
+ | `battery.not_low` | `battery.no_longer_low` |
270
+ | `lawn_mower.docked` | `lawn_mower.returned_to_dock` |
271
+ | `schedule.turned_on` | `schedule.block_started` |
272
+ | `schedule.turned_off` | `schedule.block_ended` |
273
+ | `timer.time_remaining` | `timer.remaining_time_reached` |
274
+ | `update.update_became_available` | `update.became_available` |
275
+ | `vacuum.docked` | `vacuum.returned_to_dock` |
276
+ | `climate.target_temperature` (condition) | `climate.is_target_temperature` |
277
+ | `climate.target_humidity` (condition) | `climate.is_target_humidity` |
278
+
279
+ **Discovering what exists:** every purpose-specific trigger and condition (and every service action) has a dedicated documentation page covering its config shape, options, and examples — fetch it on demand instead of guessing keys; see `domain-docs.md#fetching-trigger-condition-and-action-docs`. The trees span 50+ domains (~190 trigger and ~145 condition pages, generic types included): battery, motion, occupancy, door/window/gate/garage_door, climate, media_player, sun, timer, schedule, vacuum, lawn_mower, zone, and more. 2026.7 added the sun family (`sun.dawn`, `sun.dusk`, `sun.solar_noon`, `sun.solar_midnight`, elevation triggers).
280
+
281
+ Since 2026.7, `options.for` durations on **conditions** are primed from recorded history, so a freshly created or reloaded condition does not restart its duration clock from zero. Trigger `for:` clocks still reset as described in [`for:` duration resets](#for-duration-resets-on-restart-and-on-unavailable).
282
+
283
+ The generic triggers below remain fully supported and are the right choice when no purpose-specific block matches the intent (attribute changes, template-derived conditions, webhooks, MQTT, etc.). As with everything in this file, create and update automations through the config API — the YAML shows the config shape, not a file to hand-edit.
241
284
 
242
285
  ### State Trigger
243
286
 
@@ -342,6 +385,10 @@ only the real start/stop events move it.
342
385
  When several entities' `last_changed` all jumped to the same time — one that isn't sunrise/sunset —
343
386
  that was a restart re-stamp, not a real state change.
344
387
 
388
+ > 2026.7+ primes `options.for` duration tracking for purpose-specific **conditions** from recorded
389
+ > history, so those don't restart from zero on creation/reload. The resets described here still
390
+ > apply to trigger `for:` clauses and to `last_changed`-based template math.
391
+
345
392
  ### Time Trigger
346
393
 
347
394
  Fires at specific times.
@@ -461,9 +508,9 @@ triggers:
461
508
 
462
509
  To *check* zone membership in a condition rather than trigger on the transition, see [Zone Condition](#zone-condition).
463
510
 
464
- ### Native Zone Triggers & Conditions (2026.6, Labs)
511
+ ### Native Zone Triggers & Conditions (2026.6+)
465
512
 
466
- 2026.6 adds native, **any-zone** zone primitives (not just home). They are **Labs-gated** in the editor (Settings System Labs) but use the standard core config schema, so they round-trip through the config API regardless of the Labs toggle. Unlike most purpose-specific triggers, these serialize as a *new* `<domain>.<subtype>` YAML shape with `options:` and `target:` blocks:
513
+ 2026.6 added native, **any-zone** zone primitives (not just home); with the rest of the purpose-specific family they became default (non-Labs) in 2026.7. They serialize as the standard `<domain>.<name>` shape with `options:` and `target:` blocks:
467
514
 
468
515
  - **Triggers:** `zone.entered`, `zone.left`, `zone.occupancy_detected`, `zone.occupancy_cleared`
469
516
  - **Conditions:** `zone.in_zone`, `zone.not_in_zone`, `zone.occupancy_is_detected`, `zone.occupancy_is_not_detected`
@@ -480,7 +527,7 @@ triggers:
480
527
  # for: "00:05:00" # optional dwell time
481
528
  ```
482
529
 
483
- Triggers take `behavior: each` (default), `all`, or `first`; the matching **conditions** use a *different* enum — `behavior: any` (default) or `all`. These are Labs keys and may change before they stabilize, so verify against the current schema. The classic flat [Zone Trigger](#zone-trigger) / [Zone Condition](#zone-condition) and plain `state` triggers remain the stable, non-Labs fallback.
530
+ Triggers take `behavior: each` (default), `all`, or `first`; the matching **conditions** use a *different* enum — `behavior: any` (default) or `all` (see [Purpose-Specific Triggers & Conditions](#purpose-specific-triggers--conditions-default-since-20267)). The classic flat [Zone Trigger](#zone-trigger) / [Zone Condition](#zone-condition) and plain `state` triggers remain supported.
484
531
 
485
532
  > The `entered_home`/`left_home`/`is_home`/`is_not_home` device automations were removed in **2026.5** (see [below](#presence-and-person-triggers-and-conditions-removed-in-20265)) — 2026.6 only *adds* these any-zone primitives as the native successor.
486
533
 
@@ -612,40 +659,44 @@ entity_id: person.john
612
659
  state: "home"
613
660
  ```
614
661
 
615
- For non-home zone presence, see `#zone-condition`.
662
+ 2026.7 presence semantics to keep in mind:
663
+
664
+ - A person located by a home presence scanner (router/Bluetooth) may report **no latitude/longitude** — don't read coordinates to infer home presence; use the state or the `in_zones` attribute.
665
+ - Person and device_tracker entities expose `in_zones` (all zones containing them, smallest first). Zone entity states (person counts) are derived from it, so a person counts in **every** overlapping zone they're inside, not just one.
666
+ - A position-aware device_tracker's state is the **smallest** containing zone (previously: the zone whose center is closest).
667
+
668
+ For non-home zone presence, prefer the native zone family (see [Native Zone Triggers & Conditions](#native-zone-triggers--conditions-20266)) or `#zone-condition`.
616
669
 
617
670
  ### Timer Entity Triggers (2026.5+)
618
671
 
619
- Timer entities now expose lifecycle events as purpose-specific triggers in the UI editor. In YAML, use the `event` trigger with the corresponding `timer.*` event type.
672
+ Timer lifecycle events are purpose-specific triggers: `timer.started`, `timer.finished`, `timer.paused`, `timer.restarted`, `timer.cancelled`, plus `timer.remaining_time_reached` (renamed from `timer.time_remaining` in 2026.7), which fires at a set remaining time.
620
673
 
621
674
  ```yaml
622
- # Timer finished (also: timer.started, timer.paused, timer.restarted, timer.cancelled)
623
675
  triggers:
624
- - trigger: event
625
- event_type: timer.finished
626
- event_data:
676
+ - trigger: timer.remaining_time_reached
677
+ target:
627
678
  entity_id: timer.cooking
679
+ options:
680
+ remaining: "00:05:00"
628
681
  ```
629
682
 
683
+ The legacy `event`-trigger form (`event_type: timer.finished` with `event_data: {entity_id: ...}`) still works, but the purpose-specific form supports targets and is preferred.
684
+
630
685
  ### Media Player Triggers/Conditions (2026.5+)
631
686
 
632
- Media player entities now have purpose-specific triggers and conditions in the UI editor covering play/pause state, mute status, and volume level. In YAML these map to standard state and numeric_state triggers.
687
+ Media players have purpose-specific triggers (`media_player.started_playing`, `media_player.paused_playing`, `media_player.stopped_playing`, `media_player.turned_on`, `media_player.turned_off`, `media_player.muted`, `media_player.unmuted`, `media_player.volume_changed`, `media_player.volume_crossed_threshold`) and matching conditions (`media_player.is_playing`, `media_player.is_paused`, `media_player.is_not_playing`, `media_player.is_on`, `media_player.is_off`, `media_player.is_muted`, `media_player.is_unmuted`, `media_player.is_volume`).
633
688
 
634
689
  ```yaml
635
- # Play/pause state — to: "playing", "paused", "idle", "off"
636
- triggers:
637
- - trigger: state
638
- entity_id: media_player.living_room
639
- to: "playing"
640
-
641
- # Volume threshold
642
690
  triggers:
643
- - trigger: numeric_state
644
- entity_id: media_player.living_room
645
- attribute: volume_level
646
- above: 0.7
691
+ - trigger: media_player.started_playing
692
+ target:
693
+ entity_id: media_player.living_room
694
+ options:
695
+ for: "00:00:30" # optional: playback must continue this long
647
696
  ```
648
697
 
698
+ The generic forms still apply where no purpose-specific block fits (e.g. `state` with `to: "idle"`, or `numeric_state` on `attribute: volume_level`).
699
+
649
700
  ---
650
701
 
651
702
  ## Wait Actions