ha-mcp-dev 7.9.0.dev758__tar.gz → 7.9.0.dev760__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.9.0.dev758/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.9.0.dev760}/PKG-INFO +9 -2
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/README.md +8 -1
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/pyproject.toml +1 -1
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/client/rest_client.py +28 -8
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/read_only.py +18 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/backup.py +14 -2
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/best_practice_checker.py +73 -2
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_config_automations.py +2 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_traces.py +7 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_updates.py +254 -56
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_utility.py +25 -2
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/update_check.py +2 -2
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/utils/domain_handlers.py +10 -1
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/utils/kill_signal_diagnostics.py +11 -11
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760/src/ha_mcp_dev.egg-info}/PKG-INFO +9 -2
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/LICENSE +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/setup.cfg +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/_version.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/backup_manager.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/config.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/policy/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/policy/approval_queue.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/policy/evaluator.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/policy/handlers.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/policy/middleware.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/policy/model.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/policy/persistence.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/policy/value_sources.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/settings_ui/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/settings_ui/settings.css +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/settings_ui/settings.html +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/settings_ui/settings.js +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/auto_backup.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/config_entry_flow.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/radio/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/radio/base.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/radio/matter.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/radio/thread.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/radio/zigbee.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/radio/zwave.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/smart_search/_base.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/smart_search/_config.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_integrations.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_radio.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_themes.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/validation_middleware.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/utils/skill_loader.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/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.9.0.
|
|
3
|
+
Version: 7.9.0.dev760
|
|
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
|
|
@@ -462,7 +462,14 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
|
|
|
462
462
|
- **[@L1AD](https://github.com/L1AD)** — Filed #966 proposing tool security policies; pointed to PolicyLayer's MCP-security work as prior art that inspired the predicate DSL shape.
|
|
463
463
|
- **[@nightcityblade](https://github.com/nightcityblade)** — Updated stale Home Assistant Advanced Mode references after HA 2026.6 made formerly advanced options available by default (#1533).
|
|
464
464
|
- **[@emmelutzer](https://github.com/emmelutzer)** — Financial support via [GitHub Sponsors](https://github.com/sponsors/julienld). Thank you! ☕
|
|
465
|
-
|
|
465
|
+
- **[@pkkr](https://github.com/pkkr)** — `ha_knx_get_project` tool exposing KNX group addresses from an uploaded ETS project file.
|
|
466
|
+
- **[@cbowns](https://github.com/cbowns)** — Fixed inconsistent hyphen in setup.astro Codex CLI docs.
|
|
467
|
+
- **[@Shaan-alpha](https://github.com/Shaan-alpha)** — Extended `ha_restart` known-good error patterns to cover 502/503 responses from reverse proxies.
|
|
468
|
+
- **[@rebelancap](https://github.com/rebelancap)** — Fixed UTC-to-local timezone conversion in `add_timezone_metadata`.
|
|
469
|
+
- **[@saevras](https://github.com/saevras)** — Fixed blueprint import E2E test to use local URL instead of host-to-container networking.
|
|
470
|
+
- **[@jasonjhofmann](https://github.com/jasonjhofmann)** — Recurring calendar events via `rrule` support in `ha_config_set_calendar_event`.
|
|
471
|
+
- **[@vpciii](https://github.com/vpciii)** — Coerce JSON-encoded strings on dict/list tool params.
|
|
472
|
+
- **[@pburtchaell](https://github.com/pburtchaell)** — Financial support via [GitHub Sponsors](https://github.com/sponsors/julienld). Thank you! ☕
|
|
466
473
|
---
|
|
467
474
|
|
|
468
475
|
## 💬 Community
|
|
@@ -430,7 +430,14 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
|
|
|
430
430
|
- **[@L1AD](https://github.com/L1AD)** — Filed #966 proposing tool security policies; pointed to PolicyLayer's MCP-security work as prior art that inspired the predicate DSL shape.
|
|
431
431
|
- **[@nightcityblade](https://github.com/nightcityblade)** — Updated stale Home Assistant Advanced Mode references after HA 2026.6 made formerly advanced options available by default (#1533).
|
|
432
432
|
- **[@emmelutzer](https://github.com/emmelutzer)** — Financial support via [GitHub Sponsors](https://github.com/sponsors/julienld). Thank you! ☕
|
|
433
|
-
|
|
433
|
+
- **[@pkkr](https://github.com/pkkr)** — `ha_knx_get_project` tool exposing KNX group addresses from an uploaded ETS project file.
|
|
434
|
+
- **[@cbowns](https://github.com/cbowns)** — Fixed inconsistent hyphen in setup.astro Codex CLI docs.
|
|
435
|
+
- **[@Shaan-alpha](https://github.com/Shaan-alpha)** — Extended `ha_restart` known-good error patterns to cover 502/503 responses from reverse proxies.
|
|
436
|
+
- **[@rebelancap](https://github.com/rebelancap)** — Fixed UTC-to-local timezone conversion in `add_timezone_metadata`.
|
|
437
|
+
- **[@saevras](https://github.com/saevras)** — Fixed blueprint import E2E test to use local URL instead of host-to-container networking.
|
|
438
|
+
- **[@jasonjhofmann](https://github.com/jasonjhofmann)** — Recurring calendar events via `rrule` support in `ha_config_set_calendar_event`.
|
|
439
|
+
- **[@vpciii](https://github.com/vpciii)** — Coerce JSON-encoded strings on dict/list tool params.
|
|
440
|
+
- **[@pburtchaell](https://github.com/pburtchaell)** — Financial support via [GitHub Sponsors](https://github.com/sponsors/julienld). Thank you! ☕
|
|
434
441
|
---
|
|
435
442
|
|
|
436
443
|
## 💬 Community
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ha-mcp-dev"
|
|
7
|
-
version = "7.9.0.
|
|
7
|
+
version = "7.9.0.dev760"
|
|
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"
|
|
@@ -606,7 +606,7 @@ class HomeAssistantClient:
|
|
|
606
606
|
self._supervised_detected = is_supervised
|
|
607
607
|
return is_supervised
|
|
608
608
|
|
|
609
|
-
async def get_addon_logs(self, slug: str) -> str:
|
|
609
|
+
async def get_addon_logs(self, slug: str, lines: int | None = None) -> str:
|
|
610
610
|
"""Fetch an add-on's container logs.
|
|
611
611
|
|
|
612
612
|
Branch on ``is_running_in_addon()`` (which keys off ``SUPERVISOR_TOKEN``
|
|
@@ -621,6 +621,15 @@ class HomeAssistantClient:
|
|
|
621
621
|
HA URL), falls back to the HA Core proxy path. That path requires an
|
|
622
622
|
admin LLA but works fine when not invoked from the add-on container.
|
|
623
623
|
|
|
624
|
+
``lines`` sets the journald window Supervisor serves via its
|
|
625
|
+
``?lines=`` query param (supervisor/api/host.py). Without it,
|
|
626
|
+
Supervisor returns only its default window (``DEFAULT_LINES = 100``
|
|
627
|
+
in supervisor/api/const.py) — which silently capped every larger
|
|
628
|
+
caller-side limit before this param existed. Both branches carry
|
|
629
|
+
it: the direct endpoint parses the query natively, and HA Core's
|
|
630
|
+
hassio proxy forwards ``request.query`` upstream
|
|
631
|
+
(homeassistant/components/hassio/http.py).
|
|
632
|
+
|
|
624
633
|
Both branches return ``text/plain`` log content.
|
|
625
634
|
|
|
626
635
|
Raises:
|
|
@@ -633,17 +642,18 @@ class HomeAssistantClient:
|
|
|
633
642
|
HomeAssistantConnectionError: Network, timeout, or transport error.
|
|
634
643
|
"""
|
|
635
644
|
if is_running_in_addon():
|
|
636
|
-
return await self._get_addon_logs_via_supervisor(slug)
|
|
645
|
+
return await self._get_addon_logs_via_supervisor(slug, lines=lines)
|
|
637
646
|
|
|
638
647
|
logger.debug(f"Fetching addon logs for slug={slug} via HA Core proxy")
|
|
639
648
|
response = await self._raw_request(
|
|
640
649
|
"GET",
|
|
641
650
|
f"/hassio/addons/{slug}/logs",
|
|
642
651
|
headers={"Accept": "text/plain"},
|
|
652
|
+
params={"lines": lines} if lines is not None else None,
|
|
643
653
|
)
|
|
644
654
|
return response.text
|
|
645
655
|
|
|
646
|
-
async def _supervisor_logs_get(self, path: str) -> str:
|
|
656
|
+
async def _supervisor_logs_get(self, path: str, lines: int | None = None) -> str:
|
|
647
657
|
"""Fetch ``text/plain`` logs from a Supervisor REST endpoint.
|
|
648
658
|
|
|
649
659
|
``path`` is everything between ``http://supervisor/`` and ``/logs``:
|
|
@@ -652,6 +662,9 @@ class HomeAssistantClient:
|
|
|
652
662
|
- ``"<service>"`` (where service ∈ {supervisor, host, core, dns, audio,
|
|
653
663
|
cli, multicast, observer}) for system-service logs
|
|
654
664
|
|
|
665
|
+
``lines`` maps to the endpoint's ``?lines=`` journald-window query
|
|
666
|
+
param; omitted → Supervisor's 100-line default window.
|
|
667
|
+
|
|
655
668
|
Bypasses ``HomeAssistantClient.httpx_client`` because the Supervisor
|
|
656
669
|
endpoint uses a different base URL (``http://supervisor``) and a
|
|
657
670
|
different token (``SUPERVISOR_TOKEN``) than HA Core REST. Both
|
|
@@ -681,9 +694,10 @@ class HomeAssistantClient:
|
|
|
681
694
|
|
|
682
695
|
relative_path = f"/{path}/logs"
|
|
683
696
|
logger.debug(
|
|
684
|
-
"Fetching %s%s via Supervisor direct",
|
|
697
|
+
"Fetching %s%s via Supervisor direct (lines=%s)",
|
|
685
698
|
get_supervisor_base_url(),
|
|
686
699
|
relative_path,
|
|
700
|
+
lines,
|
|
687
701
|
)
|
|
688
702
|
|
|
689
703
|
try:
|
|
@@ -694,6 +708,7 @@ class HomeAssistantClient:
|
|
|
694
708
|
response = await client.get(
|
|
695
709
|
relative_path,
|
|
696
710
|
headers={"Accept": "text/plain"},
|
|
711
|
+
params={"lines": lines} if lines is not None else None,
|
|
697
712
|
)
|
|
698
713
|
except httpx.TimeoutException as e:
|
|
699
714
|
raise HomeAssistantConnectionError(
|
|
@@ -753,7 +768,9 @@ class HomeAssistantClient:
|
|
|
753
768
|
)
|
|
754
769
|
return response.text
|
|
755
770
|
|
|
756
|
-
async def _get_addon_logs_via_supervisor(
|
|
771
|
+
async def _get_addon_logs_via_supervisor(
|
|
772
|
+
self, slug: str, lines: int | None = None
|
|
773
|
+
) -> str:
|
|
757
774
|
"""Fetch add-on container logs directly from Supervisor's REST API.
|
|
758
775
|
|
|
759
776
|
Distinct from ``tools_bug_report._fetch_addon_logs``: that helper is
|
|
@@ -766,9 +783,11 @@ class HomeAssistantClient:
|
|
|
766
783
|
Delegates to ``_supervisor_logs_get`` so error handling stays in
|
|
767
784
|
lockstep with ``_get_system_service_logs``.
|
|
768
785
|
"""
|
|
769
|
-
return await self._supervisor_logs_get(f"addons/{slug}")
|
|
786
|
+
return await self._supervisor_logs_get(f"addons/{slug}", lines=lines)
|
|
770
787
|
|
|
771
|
-
async def _get_system_service_logs(
|
|
788
|
+
async def _get_system_service_logs(
|
|
789
|
+
self, service: str, lines: int | None = None
|
|
790
|
+
) -> str:
|
|
772
791
|
"""Fetch HA system-service logs.
|
|
773
792
|
|
|
774
793
|
``service`` must be one of the eight Supervisor-managed services:
|
|
@@ -796,13 +815,14 @@ class HomeAssistantClient:
|
|
|
796
815
|
logs) call kept working through its own Core-proxy fallback.
|
|
797
816
|
"""
|
|
798
817
|
if is_running_in_addon():
|
|
799
|
-
return await self._supervisor_logs_get(service)
|
|
818
|
+
return await self._supervisor_logs_get(service, lines=lines)
|
|
800
819
|
|
|
801
820
|
logger.debug(f"Fetching {service} logs via HA Core proxy")
|
|
802
821
|
response = await self._raw_request(
|
|
803
822
|
"GET",
|
|
804
823
|
f"/hassio/{service}/logs",
|
|
805
824
|
headers={"Accept": "text/plain"},
|
|
825
|
+
params={"lines": lines} if lines is not None else None,
|
|
806
826
|
)
|
|
807
827
|
return response.text
|
|
808
828
|
|
|
@@ -131,6 +131,16 @@ def _custom_tool_write(args: dict[str, Any]) -> str | None:
|
|
|
131
131
|
return "sandbox code execution"
|
|
132
132
|
|
|
133
133
|
|
|
134
|
+
def _updates_write(args: dict[str, Any]) -> str | None:
|
|
135
|
+
# ``action`` defaults to "list" at the schema layer, so a missing action
|
|
136
|
+
# executes as a read (the middleware sees RAW pre-validation arguments —
|
|
137
|
+
# an absent key here means the tool itself will run the list branch).
|
|
138
|
+
action = args.get("action")
|
|
139
|
+
if action in (None, "list", "get"):
|
|
140
|
+
return None
|
|
141
|
+
return f"action={action!r}"
|
|
142
|
+
|
|
143
|
+
|
|
134
144
|
def _radio_write(args: dict[str, Any]) -> str | None:
|
|
135
145
|
action = args.get("action")
|
|
136
146
|
# Reads (allowed): per-node diagnostics, the integration/network summary,
|
|
@@ -200,6 +210,14 @@ READ_ONLY_EXEMPT_TOOLS: dict[str, ReadOnlyExemption] = {
|
|
|
200
210
|
"cluster-attribute read ('cluster_read'), and the Thread dataset "
|
|
201
211
|
"listing ('list_datasets')",
|
|
202
212
|
),
|
|
213
|
+
# Update listing/details exist only here — ha_get_updates was merged
|
|
214
|
+
# into this tool (issue #1726), so hiding it would remove the read
|
|
215
|
+
# surface entirely.
|
|
216
|
+
"ha_manage_updates": ReadOnlyExemption(
|
|
217
|
+
_updates_write,
|
|
218
|
+
"listing pending updates (action='list', the default) and reading "
|
|
219
|
+
"update details/release notes (action='get')",
|
|
220
|
+
),
|
|
203
221
|
}
|
|
204
222
|
|
|
205
223
|
|
|
@@ -329,11 +329,19 @@ async def _poll_backup_completion(
|
|
|
329
329
|
)
|
|
330
330
|
|
|
331
331
|
if event_state == "failed":
|
|
332
|
+
# Surface the failure event verbatim — HA's failed event
|
|
333
|
+
# carries the cause (e.g. another backup already in
|
|
334
|
+
# progress), and dropping it left CI failures reading
|
|
335
|
+
# "Backup creation failed: Backup creation failed" with
|
|
336
|
+
# nothing to diagnose.
|
|
332
337
|
raise_tool_error(
|
|
333
338
|
create_error_response(
|
|
334
339
|
ErrorCode.SERVICE_CALL_FAILED,
|
|
335
340
|
"Backup creation failed",
|
|
336
|
-
context={
|
|
341
|
+
context={
|
|
342
|
+
"backup_job_id": backup_job_id,
|
|
343
|
+
"last_action_event": last_event,
|
|
344
|
+
},
|
|
337
345
|
)
|
|
338
346
|
)
|
|
339
347
|
|
|
@@ -410,7 +418,11 @@ async def _poll_backup_completion(
|
|
|
410
418
|
create_error_response(
|
|
411
419
|
ErrorCode.SERVICE_CALL_FAILED,
|
|
412
420
|
"Backup creation failed (observed at post-timeout lookup)",
|
|
413
|
-
context={
|
|
421
|
+
context={
|
|
422
|
+
"backup_job_id": backup_job_id,
|
|
423
|
+
"name": name,
|
|
424
|
+
"last_action_event": last_event,
|
|
425
|
+
},
|
|
414
426
|
)
|
|
415
427
|
)
|
|
416
428
|
if state != "idle":
|
{ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/best_practice_checker.py
RENAMED
|
@@ -162,6 +162,28 @@ _TARGET_FIELDS = ("entity_id", "device_id", "area_id", "floor_id", "label_id")
|
|
|
162
162
|
# ``service:`` (legacy) and ``action:`` (modern, 2024+) for the same field.
|
|
163
163
|
_SERVICE_KEYS = ("service", "action")
|
|
164
164
|
|
|
165
|
+
# Purpose-specific trigger/condition keys renamed in HA 2026.7 — the old keys
|
|
166
|
+
# no longer load (home-assistant/core#174463).
|
|
167
|
+
_RENAMED_TRIGGER_KEYS = {
|
|
168
|
+
"battery.low": "battery.became_low",
|
|
169
|
+
"battery.not_low": "battery.no_longer_low",
|
|
170
|
+
"lawn_mower.docked": "lawn_mower.returned_to_dock",
|
|
171
|
+
"schedule.turned_on": "schedule.block_started",
|
|
172
|
+
"schedule.turned_off": "schedule.block_ended",
|
|
173
|
+
"timer.time_remaining": "timer.remaining_time_reached",
|
|
174
|
+
"update.update_became_available": "update.became_available",
|
|
175
|
+
"vacuum.docked": "vacuum.returned_to_dock",
|
|
176
|
+
}
|
|
177
|
+
_RENAMED_CONDITION_KEYS = {
|
|
178
|
+
"climate.target_temperature": "climate.is_target_temperature",
|
|
179
|
+
"climate.target_humidity": "climate.is_target_humidity",
|
|
180
|
+
}
|
|
181
|
+
# Trigger ``options.behavior`` values renamed in HA 2026.7: any→each,
|
|
182
|
+
# last→all (home-assistant/core#173259). The old values still load but raise
|
|
183
|
+
# an HA repair issue and face removal. Condition ``options.behavior`` keeps
|
|
184
|
+
# ``any``/``all`` — conditions are never flagged.
|
|
185
|
+
_DEPRECATED_TRIGGER_BEHAVIOR = {"any": "each", "last": "all"}
|
|
186
|
+
|
|
165
187
|
|
|
166
188
|
# ---------------------------------------------------------------------------
|
|
167
189
|
# Public API
|
|
@@ -318,6 +340,23 @@ def _check_condition_templates(
|
|
|
318
340
|
# Shorthand template condition
|
|
319
341
|
_check_template_string(cond, warnings, skill_prefix, "condition")
|
|
320
342
|
elif isinstance(cond, dict):
|
|
343
|
+
# 2026.7 renamed purpose-specific condition keys — old keys no
|
|
344
|
+
# longer load.
|
|
345
|
+
condition_key = cond.get("condition")
|
|
346
|
+
renamed_condition = (
|
|
347
|
+
_RENAMED_CONDITION_KEYS.get(condition_key)
|
|
348
|
+
if isinstance(condition_key, str)
|
|
349
|
+
else None
|
|
350
|
+
)
|
|
351
|
+
if renamed_condition:
|
|
352
|
+
_emit(
|
|
353
|
+
warnings,
|
|
354
|
+
f"Condition key `{condition_key}` was renamed to "
|
|
355
|
+
f"`{renamed_condition}` in HA 2026.7 and the old key no "
|
|
356
|
+
f"longer loads — use `condition: {renamed_condition}`.",
|
|
357
|
+
skill_prefix,
|
|
358
|
+
"automation-patterns.md#native-conditions",
|
|
359
|
+
)
|
|
321
360
|
if cond.get("condition") == "template":
|
|
322
361
|
vt = cond.get("value_template", "")
|
|
323
362
|
if isinstance(vt, str):
|
|
@@ -655,12 +694,44 @@ def _check_triggers(
|
|
|
655
694
|
|
|
656
695
|
platform = trigger.get("platform", trigger.get("trigger", ""))
|
|
657
696
|
|
|
697
|
+
# 2026.7 renamed purpose-specific trigger keys — old keys no longer load.
|
|
698
|
+
renamed_trigger = (
|
|
699
|
+
_RENAMED_TRIGGER_KEYS.get(platform) if isinstance(platform, str) else None
|
|
700
|
+
)
|
|
701
|
+
if renamed_trigger:
|
|
702
|
+
_emit(
|
|
703
|
+
warnings,
|
|
704
|
+
f"Trigger key `{platform}` was renamed to `{renamed_trigger}` "
|
|
705
|
+
"in HA 2026.7 and the old key no longer loads — use "
|
|
706
|
+
f"`trigger: {renamed_trigger}`.",
|
|
707
|
+
skill_prefix,
|
|
708
|
+
"automation-patterns.md#trigger-types",
|
|
709
|
+
)
|
|
710
|
+
|
|
711
|
+
# 2026.7 renamed trigger `options.behavior` values (any→each, last→all).
|
|
712
|
+
options = trigger.get("options")
|
|
713
|
+
if isinstance(options, dict):
|
|
714
|
+
behavior = options.get("behavior")
|
|
715
|
+
if isinstance(behavior, str) and behavior in _DEPRECATED_TRIGGER_BEHAVIOR:
|
|
716
|
+
_emit(
|
|
717
|
+
warnings,
|
|
718
|
+
f"Trigger `options.behavior: {behavior}` was renamed to "
|
|
719
|
+
f"`{_DEPRECATED_TRIGGER_BEHAVIOR[behavior]}` in HA 2026.7 — "
|
|
720
|
+
"the old value still loads but raises a repair issue and "
|
|
721
|
+
"will be removed. Valid trigger values: `each`, `first`, "
|
|
722
|
+
"`all` (conditions keep `any`/`all`).",
|
|
723
|
+
skill_prefix,
|
|
724
|
+
"automation-patterns.md#trigger-types",
|
|
725
|
+
)
|
|
726
|
+
|
|
658
727
|
# Device trigger → prefer entity_id-based triggers
|
|
659
728
|
if platform == "device":
|
|
660
729
|
_emit(
|
|
661
730
|
warnings,
|
|
662
|
-
"Trigger uses `device` platform with `device_id` — prefer "
|
|
663
|
-
"
|
|
731
|
+
"Trigger uses `device` platform with `device_id` — prefer a "
|
|
732
|
+
"purpose-specific trigger (`<domain>.<name>` with a `target:` "
|
|
733
|
+
"of entities/areas/floors/labels, HA 2026.7+) or a `state`/"
|
|
734
|
+
"`event` trigger with `entity_id` when possible "
|
|
664
735
|
"(device_id breaks on re-add).",
|
|
665
736
|
skill_prefix,
|
|
666
737
|
"device-control.md#entity-id-vs-device-id",
|
{ha_mcp_dev-7.9.0.dev758 → ha_mcp_dev-7.9.0.dev760}/src/ha_mcp/tools/tools_config_automations.py
RENAMED
|
@@ -440,6 +440,8 @@ class AutomationConfigTools:
|
|
|
440
440
|
Field(
|
|
441
441
|
description="Complete automation configuration with required fields: 'alias', 'triggers', 'actions'. "
|
|
442
442
|
"Optional: 'description', 'conditions', 'mode', 'max', 'initial_state', 'variables'. "
|
|
443
|
+
"Purpose-specific triggers/conditions (HA 2026.7+ default: 'trigger': '<domain>.<name>' "
|
|
444
|
+
"with 'target'/'options') are valid config. "
|
|
443
445
|
"Mutually exclusive with python_transform.",
|
|
444
446
|
default=None,
|
|
445
447
|
),
|
|
@@ -712,6 +712,8 @@ def _populate_trigger_info(
|
|
|
712
712
|
)
|
|
713
713
|
if "entity_id" in trigger_vars:
|
|
714
714
|
result["trigger"]["entity_id"] = trigger_vars["entity_id"]
|
|
715
|
+
if "error" in trigger_step:
|
|
716
|
+
result["trigger"]["error"] = trigger_step["error"]
|
|
715
717
|
|
|
716
718
|
if "trigger" not in result and "trigger" in trace:
|
|
717
719
|
result["trigger"] = {"description": trace["trigger"]}
|
|
@@ -731,6 +733,11 @@ def _populate_condition_results(
|
|
|
731
733
|
}
|
|
732
734
|
if "timestamp" in cond:
|
|
733
735
|
cond_result["timestamp"] = cond["timestamp"]
|
|
736
|
+
# HA 2026.7+ always records template errors on the failing step
|
|
737
|
+
# (core #172917) — dropping this field would hide the reason a
|
|
738
|
+
# condition evaluated to None.
|
|
739
|
+
if "error" in cond:
|
|
740
|
+
cond_result["error"] = cond["error"]
|
|
734
741
|
condition_results.append(cond_result)
|
|
735
742
|
result["condition_results"] = condition_results
|
|
736
743
|
|