ha-mcp-dev 7.10.0.dev776__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.
- {ha_mcp_dev-7.10.0.dev776/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.10.0.dev777}/PKG-INFO +1 -1
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/pyproject.toml +1 -1
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/README.md +1 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +15 -5
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +79 -28
- ha_mcp_dev-7.10.0.dev777/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +271 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +119 -94
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +14 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp_dev.egg-info/SOURCES.txt +1 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/LICENSE +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/README.md +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/setup.cfg +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/_version.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/backup_manager.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/client/rest_client.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/config.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/policy/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/policy/approval_queue.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/policy/evaluator.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/policy/handlers.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/policy/middleware.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/policy/model.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/policy/persistence.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/policy/value_sources.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/read_only.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.10.0.dev776 → 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
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/settings_ui/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/settings_ui/settings.css +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/settings_ui/settings.html +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/settings_ui/settings.js +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/auto_backup.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/config_entry_flow.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/radio/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/radio/base.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/radio/matter.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/radio/thread.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/radio/zigbee.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/radio/zwave.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/smart_search/_base.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/smart_search/_config.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_config_automations.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_integrations.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_radio.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_themes.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/tools/validation_middleware.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/transport_security.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/update_check.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/utils/skill_loader.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/visibility/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/visibility/model.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/visibility/persistence.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/visibility/resolver.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/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.10.0.
|
|
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
|
{ha_mcp_dev-7.10.0.dev776 → ha_mcp_dev-7.10.0.dev777}/src/ha_mcp/resources/skills-vendor/README.md
RENAMED
|
@@ -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:
|
|
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
|
|
43
|
-
|
|
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
|
|
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.
|
|
232
|
+
### Purpose-Specific Triggers & Conditions (default since 2026.7)
|
|
233
233
|
|
|
234
|
-
|
|
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
|
-
|
|
237
|
-
|
|
238
|
-
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
|
511
|
+
### Native Zone Triggers & Conditions (2026.6+)
|
|
465
512
|
|
|
466
|
-
2026.6
|
|
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
|
|
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
|
-
|
|
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
|
|
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:
|
|
625
|
-
|
|
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
|
|
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:
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
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
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# Blueprints
|
|
2
|
+
|
|
3
|
+
A **blueprint is a reusable, parameterized automation/script/template** — a skeleton whose device- and entity-specific parts are replaced by user-provided **inputs**. Author a blueprint only when a pattern will be instantiated more than once or shared/distributed. A one-off automation should stay a plain automation; don't blueprint what you'll use once.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
1. [When to Author a Blueprint](#when-to-author-a-blueprint)
|
|
7
|
+
2. [Blueprint Metadata](#blueprint-metadata)
|
|
8
|
+
3. [Inputs and Selectors](#inputs-and-selectors)
|
|
9
|
+
4. [`target` Selector vs `entity` Selector](#target-selector-vs-entity-selector)
|
|
10
|
+
5. [Defaults](#defaults)
|
|
11
|
+
6. [Input Sections](#input-sections)
|
|
12
|
+
7. [Referencing Inputs (`!input`) and Templating](#referencing-inputs-input-and-templating)
|
|
13
|
+
8. [Versioning and Updates](#versioning-and-updates)
|
|
14
|
+
9. [Annotated Example](#annotated-example)
|
|
15
|
+
10. [Common Pitfalls](#common-pitfalls)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## When to Author a Blueprint
|
|
20
|
+
|
|
21
|
+
| Signal | Blueprint? |
|
|
22
|
+
|--------|-----------|
|
|
23
|
+
| Same logic wanted for several sensors/rooms/devices | Yes — parameterize the varying parts as inputs |
|
|
24
|
+
| Sharing/distributing to other users | Yes — a blueprint imports cleanly via `source_url` |
|
|
25
|
+
| A single automation for one specific set of entities | No — write a normal automation |
|
|
26
|
+
| Logic differs meaningfully per instance | No — a blueprint that needs many conditional inputs is usually two blueprints |
|
|
27
|
+
|
|
28
|
+
Blueprints exist for three domains, set by the `domain:` key: `automation`, `script`, and `template`. The input/selector rules below apply to all three; the surrounding body (`triggers`/`actions` vs `sequence` vs `sensor`/`binary_sensor`) is whatever that domain normally uses.
|
|
29
|
+
|
|
30
|
+
## Blueprint Metadata
|
|
31
|
+
|
|
32
|
+
Every blueprint opens with a `blueprint:` mapping:
|
|
33
|
+
|
|
34
|
+
```yaml
|
|
35
|
+
blueprint:
|
|
36
|
+
name: Motion-Activated Light # required; short and descriptive
|
|
37
|
+
description: Turn a light on when motion is detected, off after a delay.
|
|
38
|
+
domain: automation # required: automation | script | template
|
|
39
|
+
author: Your Name # optional
|
|
40
|
+
source_url: https://github.com/you/ha-blueprints/blob/main/motion_light.yaml
|
|
41
|
+
homeassistant:
|
|
42
|
+
min_version: "2024.10.0" # optional; gate version-specific features (see note below)
|
|
43
|
+
input:
|
|
44
|
+
# ... see below
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
- **`source_url`** — the canonical URL of the blueprint file. Always set it. It enables **"Re-import blueprint"** (in-place updates when you publish a fix), provides attribution, and is what the *Import Blueprint* dialog stores. A blueprint without `source_url` can't be updated from source and is awkward to share.
|
|
48
|
+
- **`min_version`** — set it when the blueprint uses a feature added in a specific release (e.g. input sections need `2024.6.0`; the in-item `action:` key replacing `service:` needs `2024.8.0`; the plural `triggers:`/`conditions:`/`actions:` keys and the in-item `trigger:` key replacing `platform:` need `2024.10.0`). HA refuses to import the blueprint on older versions instead of failing mysteriously at runtime.
|
|
49
|
+
- **`domain`** must match the body: an `automation` blueprint has `triggers:`/`actions:`, a `script` blueprint has a `sequence:`, a `template` blueprint defines template entities.
|
|
50
|
+
|
|
51
|
+
## Inputs and Selectors
|
|
52
|
+
|
|
53
|
+
Every entity, device, area, number, or option a user should customize is an **input** with a **selector**. The selector determines the UI control and, critically, *validates and constrains* what the user can pick.
|
|
54
|
+
|
|
55
|
+
**Prefer a typed selector over free text.** A `text` selector for an entity forces users to type `light.living_room` correctly by hand — a typo passes validation and fails silently at runtime. An `entity` selector gives a filtered picker that can only yield valid, existing entities.
|
|
56
|
+
|
|
57
|
+
Common selectors:
|
|
58
|
+
|
|
59
|
+
| Selector | Yields | Use for |
|
|
60
|
+
|----------|--------|---------|
|
|
61
|
+
| `entity` | entity_id(s) | Picking specific entities; filter with `domain`, `device_class`, `integration` |
|
|
62
|
+
| `target` | a target dict (entities/devices/areas/floors/labels) | Anything you pass straight to a service call's `target:` |
|
|
63
|
+
| `device` | device_id(s) | Device triggers, or device-level actions (rare — prefer entities) |
|
|
64
|
+
| `area` / `floor` / `label` | area/floor/label id(s) | Scoping actions to a whole area/floor/label |
|
|
65
|
+
| `number` | number | Timeouts, brightness, thresholds; set `min`/`max`/`step`/`mode`/`unit_of_measurement` |
|
|
66
|
+
| `boolean` | true/false | On/off toggles |
|
|
67
|
+
| `select` | a chosen option | A fixed list of choices (`options:`) |
|
|
68
|
+
| `time` / `duration` | time / duration | Schedules and delays |
|
|
69
|
+
| `action` | a sequence of actions | Letting the user inject their own actions (e.g. "what to do when triggered") |
|
|
70
|
+
| `text` | free string | Genuinely free text only (messages, names) — never entity IDs |
|
|
71
|
+
| `object` | arbitrary YAML | Advanced structured input; last resort |
|
|
72
|
+
|
|
73
|
+
Constrain entity/device selectors so users can only pick valid targets:
|
|
74
|
+
|
|
75
|
+
```yaml
|
|
76
|
+
motion_sensor:
|
|
77
|
+
name: Motion sensor
|
|
78
|
+
selector:
|
|
79
|
+
entity:
|
|
80
|
+
domain: binary_sensor
|
|
81
|
+
device_class: motion # only motion binary_sensors are offered
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Add `multiple: true` to accept a list. Filter by `integration:` when the blueprint is integration-specific.
|
|
85
|
+
|
|
86
|
+
## `target` Selector vs `entity` Selector
|
|
87
|
+
|
|
88
|
+
The issue this guide most often resolves. Both point at "what to control," but they produce different shapes:
|
|
89
|
+
|
|
90
|
+
- **`entity` selector → `entity_id`(s).** Use it when you need the id itself — for a `state` trigger's `entity_id`, a `states(...)` lookup in a template, or an action that specifically wants an entity.
|
|
91
|
+
- **`target` selector → a target dict** (`{entity_id, device_id, area_id, floor_id, label_id}`). Use it when the value flows straight into a service call's `target:`. It lets the user target entities, whole devices, or entire areas/floors/labels — more flexible for "act on these lights," where the user might prefer to say "all lights in the living room area."
|
|
92
|
+
|
|
93
|
+
```yaml
|
|
94
|
+
# target selector — pass directly to target:
|
|
95
|
+
target_light:
|
|
96
|
+
selector:
|
|
97
|
+
target:
|
|
98
|
+
entity:
|
|
99
|
+
domain: light
|
|
100
|
+
# ...
|
|
101
|
+
actions:
|
|
102
|
+
- action: light.turn_on
|
|
103
|
+
target: !input target_light # accepts entity/device/area
|
|
104
|
+
|
|
105
|
+
# entity selector — you need the id for a trigger / template
|
|
106
|
+
motion_sensor:
|
|
107
|
+
selector:
|
|
108
|
+
entity:
|
|
109
|
+
domain: binary_sensor
|
|
110
|
+
# ...
|
|
111
|
+
triggers:
|
|
112
|
+
- trigger: state
|
|
113
|
+
entity_id: !input motion_sensor # needs a concrete entity_id
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Rule of thumb: **controlling things → `target`; observing/templating a specific entity → `entity`.**
|
|
117
|
+
|
|
118
|
+
## Defaults
|
|
119
|
+
|
|
120
|
+
Provide `default:` for tuning inputs so the blueprint works out of the box:
|
|
121
|
+
|
|
122
|
+
```yaml
|
|
123
|
+
no_motion_wait:
|
|
124
|
+
name: Wait time
|
|
125
|
+
default: 120 # sensible default → user can skip it
|
|
126
|
+
selector:
|
|
127
|
+
number: { min: 0, max: 3600, unit_of_measurement: seconds, mode: slider }
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Don't** default entity/device/target selectors — there's no universally-correct entity, so the user must choose. An input with no `default:` is required; an input with a `default:` is optional. Use this deliberately: required for the entities the blueprint acts on, optional (defaulted) for behavior tuning.
|
|
131
|
+
|
|
132
|
+
## Input Sections
|
|
133
|
+
|
|
134
|
+
Blueprints with many inputs become a wall of fields. Group them with sections (HA `2024.6.0`+). A section is an input entry that itself contains an `input:` map:
|
|
135
|
+
|
|
136
|
+
```yaml
|
|
137
|
+
input:
|
|
138
|
+
devices:
|
|
139
|
+
name: Devices
|
|
140
|
+
icon: mdi:motion-sensor
|
|
141
|
+
input: # nested inputs belong to this section
|
|
142
|
+
motion_sensor: { name: Motion sensor, selector: { entity: { domain: binary_sensor, device_class: motion } } }
|
|
143
|
+
target_light: { name: Light(s), selector: { target: { entity: { domain: light } } } }
|
|
144
|
+
settings:
|
|
145
|
+
name: Settings
|
|
146
|
+
icon: mdi:cog
|
|
147
|
+
collapsed: true # collapsed by default — advanced knobs out of the way
|
|
148
|
+
input:
|
|
149
|
+
no_motion_wait: { name: Wait time, default: 120, selector: { number: { min: 0, max: 3600 } } }
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
`!input` still references inputs by their own key (`!input motion_sensor`), regardless of which section they live in — section names are for display only.
|
|
153
|
+
|
|
154
|
+
## Referencing Inputs (`!input`) and Templating
|
|
155
|
+
|
|
156
|
+
Inside the blueprint body, reference an input with the `!input` YAML tag: `entity_id: !input motion_sensor`.
|
|
157
|
+
|
|
158
|
+
**The #1 blueprint bug:** `!input` is a YAML tag, **not** a template value — you cannot write `{{ !input x }}` or drop `!input` inside a Jinja expression. To use an input in a template, first expose it as a **variable**, then reference the variable:
|
|
159
|
+
|
|
160
|
+
```yaml
|
|
161
|
+
variables:
|
|
162
|
+
brightness_pct: !input brightness # bind input → variable at script level
|
|
163
|
+
# ...
|
|
164
|
+
actions:
|
|
165
|
+
- action: light.turn_on
|
|
166
|
+
target: !input target_light
|
|
167
|
+
data:
|
|
168
|
+
brightness_pct: "{{ brightness_pct }}" # use the variable, never !input
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
For **templated triggers**, bind inputs through `trigger_variables:` (a separate top-level key evaluated before triggers fire). It supports **limited templates only** — no `states()`/`state_attr()` — and exists mainly to pass a blueprint `!input` into trigger options (see `automation-patterns.md#trigger-types`). Don't put state-based templates there.
|
|
172
|
+
|
|
173
|
+
`enabled:` on an individual trigger/condition/action also accepts a blueprint `!input` (evaluated once at load) — handy for optional behavior toggled by a `boolean` input (see `automation-patterns.md#enabled-on-individual-triggers-conditions-and-actions`).
|
|
174
|
+
|
|
175
|
+
## Versioning and Updates
|
|
176
|
+
|
|
177
|
+
Users import a blueprint once, then create automations from it. When you publish changes:
|
|
178
|
+
|
|
179
|
+
- **Renaming or removing an input key breaks every existing instance** — their stored `!input` references no longer resolve. Treat input keys as a public API.
|
|
180
|
+
- **Add** new inputs rather than changing old ones, and give every new input a `default:` so existing automations keep working after a re-import without the user touching them.
|
|
181
|
+
- Bump `min_version` if a change relies on a newer HA feature.
|
|
182
|
+
- Keep `source_url` stable — it's the update anchor. Users re-import from the same URL to pick up fixes.
|
|
183
|
+
|
|
184
|
+
## Annotated Example
|
|
185
|
+
|
|
186
|
+
A complete motion-light automation blueprint demonstrating every practice above:
|
|
187
|
+
|
|
188
|
+
```yaml
|
|
189
|
+
blueprint:
|
|
190
|
+
name: Motion-Activated Light
|
|
191
|
+
description: Turn a light (or area) on when motion is detected, off after a delay.
|
|
192
|
+
domain: automation
|
|
193
|
+
author: Your Name
|
|
194
|
+
source_url: https://github.com/you/ha-blueprints/blob/main/motion_light.yaml
|
|
195
|
+
homeassistant:
|
|
196
|
+
min_version: "2024.10.0" # modern triggers/actions syntax (input sections alone need only 2024.6.0)
|
|
197
|
+
input:
|
|
198
|
+
devices:
|
|
199
|
+
name: Devices
|
|
200
|
+
icon: mdi:motion-sensor
|
|
201
|
+
input:
|
|
202
|
+
motion_sensor:
|
|
203
|
+
name: Motion sensor
|
|
204
|
+
selector:
|
|
205
|
+
entity:
|
|
206
|
+
domain: binary_sensor
|
|
207
|
+
device_class: motion # constrained picker — no free text
|
|
208
|
+
target_light:
|
|
209
|
+
name: Light(s) to control
|
|
210
|
+
selector:
|
|
211
|
+
target: # target → user may pick lights OR an area
|
|
212
|
+
entity:
|
|
213
|
+
domain: light
|
|
214
|
+
settings:
|
|
215
|
+
name: Settings
|
|
216
|
+
icon: mdi:cog
|
|
217
|
+
collapsed: true
|
|
218
|
+
input:
|
|
219
|
+
no_motion_wait:
|
|
220
|
+
name: Wait time
|
|
221
|
+
description: Seconds to keep the light on after the last motion.
|
|
222
|
+
default: 120 # sensible default → optional input
|
|
223
|
+
selector:
|
|
224
|
+
number: { min: 0, max: 3600, unit_of_measurement: seconds, mode: slider }
|
|
225
|
+
brightness:
|
|
226
|
+
name: Brightness
|
|
227
|
+
default: 100
|
|
228
|
+
selector:
|
|
229
|
+
number: { min: 1, max: 100, unit_of_measurement: "%" }
|
|
230
|
+
|
|
231
|
+
# Body — device-specific values come only from !input, never hardcoded:
|
|
232
|
+
mode: restart # re-trigger resets the timer
|
|
233
|
+
max_exceeded: silent
|
|
234
|
+
|
|
235
|
+
variables:
|
|
236
|
+
brightness_pct: !input brightness # expose input for use in a template
|
|
237
|
+
|
|
238
|
+
triggers:
|
|
239
|
+
- trigger: state
|
|
240
|
+
entity_id: !input motion_sensor # entity selector → concrete entity_id
|
|
241
|
+
to: "on"
|
|
242
|
+
|
|
243
|
+
actions:
|
|
244
|
+
- action: light.turn_on
|
|
245
|
+
target: !input target_light # target selector → passed straight to target:
|
|
246
|
+
data:
|
|
247
|
+
brightness_pct: "{{ brightness_pct }}" # variable, not !input, inside the template
|
|
248
|
+
- wait_for_trigger:
|
|
249
|
+
- trigger: state
|
|
250
|
+
entity_id: !input motion_sensor
|
|
251
|
+
to: "off"
|
|
252
|
+
- delay:
|
|
253
|
+
seconds: !input no_motion_wait
|
|
254
|
+
- action: light.turn_off
|
|
255
|
+
target: !input target_light
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Common Pitfalls
|
|
259
|
+
|
|
260
|
+
| Pitfall | Do instead | Why |
|
|
261
|
+
|---------|-----------|-----|
|
|
262
|
+
| Free-text field for an entity/device | `entity` / `target` / `device` selector | Typed selectors validate the choice; a typo in text fails silently at runtime |
|
|
263
|
+
| Hardcoded `light.living_room` in the body | `!input` with a selector | Hardcoding defeats the entire purpose of a blueprint — it can't be reused |
|
|
264
|
+
| `!input` used directly in a template (`{{ !input x }}`) | Bind to a `variables:` entry, use the variable | `!input` is a YAML tag, not a template value — the template errors or ignores it |
|
|
265
|
+
| State-based template in `trigger_variables:` | Move logic into a template condition/trigger | `trigger_variables` allows limited templates only (no `states()`) |
|
|
266
|
+
| No `source_url` | Set it to the file's canonical URL | Enables in-place re-import/updates and sharing |
|
|
267
|
+
| No `default:` on tuning inputs | Add a sensible `default:` | Makes the input optional and the blueprint usable out of the box |
|
|
268
|
+
| `default:` on the entity the blueprint controls | Leave it required (no default) | There's no universally-correct entity; the user must choose |
|
|
269
|
+
| `entity` selector where you pass to `target:` | Use a `target` selector | `target` lets users pick areas/devices too and drops straight into `target:` |
|
|
270
|
+
| Renaming/removing input keys in an update | Add new inputs (with defaults); keep old keys | Existing automations reference keys by name and break on rename |
|
|
271
|
+
| Missing `min_version` for a used feature | Set `homeassistant.min_version` | HA blocks import on old versions instead of failing at runtime |
|