ha-mcp-dev 7.6.0.dev610__tar.gz → 7.6.0.dev611__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 (124) hide show
  1. {ha_mcp_dev-7.6.0.dev610/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.6.0.dev611}/PKG-INFO +1 -1
  2. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/pyproject.toml +1 -1
  3. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/best_practice_checker.py +45 -0
  4. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  5. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/LICENSE +0 -0
  6. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/MANIFEST.in +0 -0
  7. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/README.md +0 -0
  8. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/setup.cfg +0 -0
  9. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/__init__.py +0 -0
  10. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/__main__.py +0 -0
  11. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/_pypi_marker +0 -0
  12. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/_version.py +0 -0
  13. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/auth/__init__.py +0 -0
  14. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/auth/consent_form.py +0 -0
  15. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/auth/provider.py +0 -0
  16. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/backup_manager.py +0 -0
  17. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/client/__init__.py +0 -0
  18. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/client/rest_client.py +0 -0
  19. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/client/supervisor_client.py +0 -0
  20. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/client/websocket_client.py +0 -0
  21. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/client/websocket_listener.py +0 -0
  22. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/config.py +0 -0
  23. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/errors.py +0 -0
  24. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/policy/__init__.py +0 -0
  25. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/policy/approval_queue.py +0 -0
  26. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/policy/evaluator.py +0 -0
  27. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/policy/handlers.py +0 -0
  28. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/policy/middleware.py +0 -0
  29. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/policy/model.py +0 -0
  30. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/policy/persistence.py +0 -0
  31. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/policy/value_sources.py +0 -0
  32. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/py.typed +0 -0
  33. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  34. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  35. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  36. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  37. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  38. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  39. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  40. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  41. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  42. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  43. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  44. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  45. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  46. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  47. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  48. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  49. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  50. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  51. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  52. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  53. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  54. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  55. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/server.py +0 -0
  56. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/settings_ui.py +0 -0
  57. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/smoke_test.py +0 -0
  58. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  59. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/__init__.py +0 -0
  60. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/auto_backup.py +0 -0
  61. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/backup.py +0 -0
  62. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/device_control.py +0 -0
  63. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/enhanced.py +0 -0
  64. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/helpers.py +0 -0
  65. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/reference_validator.py +0 -0
  66. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/registry.py +0 -0
  67. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/smart_search.py +0 -0
  68. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_addons.py +0 -0
  69. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_areas.py +0 -0
  70. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  71. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  72. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_calendar.py +0 -0
  73. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_camera.py +0 -0
  74. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_categories.py +0 -0
  75. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_code.py +0 -0
  76. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  77. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  78. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
  79. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  80. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  81. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  82. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_energy.py +0 -0
  83. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_entities.py +0 -0
  84. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  85. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_groups.py +0 -0
  86. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_hacs.py +0 -0
  87. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_history.py +0 -0
  88. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_integrations.py +0 -0
  89. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_labels.py +0 -0
  90. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
  91. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_registry.py +0 -0
  92. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_resources.py +0 -0
  93. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_search.py +0 -0
  94. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_service.py +0 -0
  95. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_services.py +0 -0
  96. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_system.py +0 -0
  97. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_todo.py +0 -0
  98. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_traces.py +0 -0
  99. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_updates.py +0 -0
  100. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_utility.py +0 -0
  101. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  102. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  103. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/tools_zones.py +0 -0
  104. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/tools/util_helpers.py +0 -0
  105. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/transforms/__init__.py +0 -0
  106. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/transforms/categorized_search.py +0 -0
  107. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  108. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/utils/__init__.py +0 -0
  109. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/utils/config_hash.py +0 -0
  110. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/utils/data_paths.py +0 -0
  111. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/utils/domain_handlers.py +0 -0
  112. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  113. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  114. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/utils/operation_manager.py +0 -0
  115. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/utils/python_sandbox.py +0 -0
  116. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp/utils/usage_logger.py +0 -0
  117. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
  118. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  119. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  120. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  121. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  122. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/tests/__init__.py +0 -0
  123. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/tests/test_constants.py +0 -0
  124. {ha_mcp_dev-7.6.0.dev610 → ha_mcp_dev-7.6.0.dev611}/tests/test_env_manager.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.6.0.dev610
3
+ Version: 7.6.0.dev611
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.6.0.dev610"
7
+ version = "7.6.0.dev611"
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"
@@ -77,6 +77,13 @@ _RE_SUN = re.compile(r"(?:is_state|state_attr|states)\s*\(\s*['\"]sun\.sun['\"]"
77
77
  _RE_STATE_IN = re.compile(r"states\s*\([^)]+\)\s+in\s+[\[(]")
78
78
  # Unsafe direct state access: states.sensor.x.state
79
79
  _RE_DIRECT_STATE = re.compile(r"\bstates\.\w+\.\w+\.state\b")
80
+ # Duration/recency checks via last_changed or last_updated arithmetic.
81
+ # Both alternations require at least one dotted qualifier (e.g. ``states.sensor.x.``)
82
+ # so bare Jinja variables named ``last_changed`` are not falsely flagged.
83
+ _RE_DURATION_MATH = re.compile(
84
+ r"\bnow\(\)\s*-\s*(?:\w+\.)+last_(?:changed|updated)\b"
85
+ r"|\b(?:\w+\.)+last_(?:changed|updated)\s*<\s*now\(\)"
86
+ )
80
87
  # Motion entity pattern
81
88
  _RE_MOTION = re.compile(r"binary_sensor\.\w*motion", re.IGNORECASE)
82
89
  # Any Jinja template marker — catch-all and target-field scan.
@@ -273,6 +280,16 @@ def _check_template_string(
273
280
  "function instead (returns 'unknown' if missing rather than raising)."
274
281
  + _ref(skill_prefix, "template-guidelines.md#common-patterns")
275
282
  )
283
+ if _RE_DURATION_MATH.search(template):
284
+ warnings.append(
285
+ f"{label} uses template for duration/recency check "
286
+ "(`now() - X.last_changed/last_updated`) — use the native `for:` field "
287
+ "on a `state` trigger or condition instead "
288
+ "(e.g., `platform: state, entity_id: binary_sensor.motion, to: 'off', "
289
+ "for: {minutes: 5}`). Native `for:` is event-driven and avoids repeated "
290
+ "template evaluation on every state change."
291
+ + _ref(skill_prefix, "automation-patterns.md#native-conditions")
292
+ )
276
293
 
277
294
  # Generic fallback: any Jinja in this logic position that didn't match
278
295
  # a specific detector. Catches new anti-patterns (issue #1011) and
@@ -505,6 +522,19 @@ def _check_triggers(
505
522
  "automation-patterns.md#trigger-types",
506
523
  )
507
524
  )
525
+ if _RE_DURATION_MATH.search(vt):
526
+ warnings.append(
527
+ "Trigger uses template for duration/recency check "
528
+ "(`now() - X.last_changed/last_updated`) — use the native "
529
+ "`for:` field on a `state` trigger instead "
530
+ "(e.g., `platform: state, entity_id: binary_sensor.motion, "
531
+ "to: 'off', for: {minutes: 5}`). Native `for:` is event-driven "
532
+ "and doesn't re-evaluate on every state change."
533
+ + _ref(
534
+ skill_prefix,
535
+ "automation-patterns.md#trigger-types",
536
+ )
537
+ )
508
538
  # Generic fallback for unmatched template triggers.
509
539
  if len(warnings) == initial and _RE_ANY_TEMPLATE.search(vt):
510
540
  warnings.append(
@@ -518,6 +548,21 @@ def _check_triggers(
518
548
  )
519
549
  )
520
550
 
551
+ # numeric_state trigger: value_template can also contain duration math
552
+ # (e.g. transforming last_changed into a seconds value for the threshold).
553
+ if platform == "numeric_state":
554
+ vt = trigger.get("value_template", "")
555
+ if isinstance(vt, str) and _RE_DURATION_MATH.search(vt):
556
+ warnings.append(
557
+ "`numeric_state` trigger uses `value_template` for duration/recency "
558
+ "check (`now() - X.last_changed/last_updated`) — use the native "
559
+ "`for:` field on a `state` trigger instead "
560
+ "(e.g., `platform: state, entity_id: binary_sensor.motion, "
561
+ "to: 'off', for: {minutes: 5}`). Native `for:` is event-driven "
562
+ "and doesn't re-evaluate on every state change."
563
+ + _ref(skill_prefix, "automation-patterns.md#trigger-types")
564
+ )
565
+
521
566
 
522
567
  # ---------------------------------------------------------------------------
523
568
  # Mode + motion check
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.6.0.dev610
3
+ Version: 7.6.0.dev611
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