ha-mcp-dev 7.9.0.dev761__tar.gz → 7.9.0.dev763__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.dev761 → ha_mcp_dev-7.9.0.dev763}/PKG-INFO +33 -4
- ha_mcp_dev-7.9.0.dev761/src/ha_mcp_dev.egg-info/PKG-INFO → ha_mcp_dev-7.9.0.dev763/README.md +29 -33
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/pyproject.toml +21 -3
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/_version.py +21 -1
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/config.py +96 -5
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/settings_ui/settings.css +76 -12
- ha_mcp_dev-7.9.0.dev761/README.md → ha_mcp_dev-7.9.0.dev763/src/ha_mcp_dev.egg-info/PKG-INFO +62 -1
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp_dev.egg-info/requires.txt +1 -1
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/LICENSE +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/setup.cfg +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/backup_manager.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/client/rest_client.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/policy/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/policy/approval_queue.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/policy/evaluator.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/policy/handlers.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/policy/middleware.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/policy/model.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/policy/persistence.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/policy/value_sources.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/read_only.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/settings_ui/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/settings_ui/settings.html +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/settings_ui/settings.js +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/auto_backup.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/config_entry_flow.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/radio/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/radio/base.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/radio/matter.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/radio/thread.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/radio/zigbee.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/radio/zwave.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/smart_search/_base.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/smart_search/_config.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_config_automations.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_integrations.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_radio.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_themes.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/validation_middleware.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/update_check.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/utils/skill_loader.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/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.dev763
|
|
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
|
|
@@ -13,9 +13,10 @@ Classifier: Intended Audience :: Developers
|
|
|
13
13
|
Classifier: License :: OSI Approved :: MIT License
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
17
|
Classifier: Topic :: Home Automation
|
|
17
18
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
-
Requires-Python: <3.
|
|
19
|
+
Requires-Python: <3.15,>=3.13
|
|
19
20
|
Description-Content-Type: text/markdown
|
|
20
21
|
License-File: LICENSE
|
|
21
22
|
Requires-Dist: fastmcp==3.4.2
|
|
@@ -24,7 +25,7 @@ Requires-Dist: pydantic==2.13.4
|
|
|
24
25
|
Requires-Dist: python-dotenv==1.2.2
|
|
25
26
|
Requires-Dist: truststore==0.10.4
|
|
26
27
|
Requires-Dist: websockets==16.0
|
|
27
|
-
Requires-Dist: cryptography
|
|
28
|
+
Requires-Dist: cryptography<50,>=48.0.0
|
|
28
29
|
Requires-Dist: pydantic-monty==0.0.18
|
|
29
30
|
Requires-Dist: tzdata>=2024.1
|
|
30
31
|
Requires-Dist: packaging>=24.0
|
|
@@ -92,6 +93,21 @@ Running Home Assistant OS? Run ha-mcp **inside** Home Assistant — no access to
|
|
|
92
93
|
|
|
93
94
|
> ⚠️ **Two ways to run ha-mcp — don't mix them.** The add-on above is the recommended path. The local **stdio** path below runs ha-mcp on your own computer; it is more advanced, has fewer features, and is behind most connection problems. If you use the add-on, your Claude Desktop config points at the add-on URL through `mcp-proxy` with **no token** — do **not** also keep a local `uvx ha-mcp@latest` entry (with `HOMEASSISTANT_URL` / `HOMEASSISTANT_TOKEN`) in your Claude Desktop config. Running both at once is a known cause of connection hangs.
|
|
94
95
|
|
|
96
|
+
<details>
|
|
97
|
+
<summary><b>Run inside Home Assistant (Container / Core — no add-on)</b></summary>
|
|
98
|
+
|
|
99
|
+
Not on Home Assistant OS? Home Assistant **Container** and **Core** installs can't run add-ons, but the **HA-MCP Custom Component** (`ha_mcp_tools`) can run the **full ha-mcp server in-process**, inside Home Assistant — no separate Docker container, no token to manage. It works on Home Assistant OS too, and coexists with the add-on. The connect URL is a Home Assistant webhook, so it reaches remote MCP clients through **Nabu Casa** (or any reverse proxy) with no extra tunnel.
|
|
100
|
+
|
|
101
|
+
1. Install **HA-MCP Custom Component** from HACS (the same repository you use for ha-mcp), or copy `custom_components/ha_mcp_tools` from this repository into your Home Assistant `config/custom_components/` directory; then restart Home Assistant
|
|
102
|
+
2. Add the integration (**Settings → Devices & Services → Add Integration → HA-MCP Custom Component**) and choose **HA-MCP Server** from the menu, then submit — creating the entry starts the server
|
|
103
|
+
3. Copy the connect URL from the **HA-MCP Server** notification (also shown on the entry's Configure screen)
|
|
104
|
+
4. Manage the server from the **HA-MCP** sidebar panel (admin-only web settings UI)
|
|
105
|
+
5. Connect your AI client to that URL
|
|
106
|
+
|
|
107
|
+
[Full in-process server documentation →](docs/in-process-server.md)
|
|
108
|
+
|
|
109
|
+
</details>
|
|
110
|
+
|
|
95
111
|
### 💻 Run locally with Claude Desktop (stdio · advanced)
|
|
96
112
|
|
|
97
113
|
*No paid subscription required.* This runs ha-mcp on your own machine over stdio.
|
|
@@ -298,7 +314,7 @@ These tools also require feature flags: `HAMCP_ENABLE_FILESYSTEM_TOOLS=true` (fi
|
|
|
298
314
|
|
|
299
315
|
To add manually: open **HACS** > **Integrations** > three-dot menu > **Custom repositories** > add `https://github.com/homeassistant-ai/ha-mcp` (category: Integration) > **Download**.
|
|
300
316
|
|
|
301
|
-
After installing, restart Home Assistant. Then open **Settings** > **Devices & Services** > **Add Integration** and search for **
|
|
317
|
+
After installing, restart Home Assistant. Then open **Settings** > **Devices & Services** > **Add Integration** and search for **HA-MCP Custom Component**.
|
|
302
318
|
|
|
303
319
|
On **Home Assistant OS / Supervised**, the integration offers to add the add-on repository and install and start the **Home Assistant MCP Server** add-on for you — no need to add the add-on repository by hand. On **Container / Core** installs (no Supervisor) there is no add-on; run the server via Docker or pip and the integration just sets up the file/YAML services.
|
|
304
320
|
|
|
@@ -306,6 +322,19 @@ On **Home Assistant OS / Supervised**, the integration offers to add the add-on
|
|
|
306
322
|
|
|
307
323
|
Copy `custom_components/ha_mcp_tools/` from this repository into your HA `config/custom_components/` directory. Restart Home Assistant, then add the integration as described above.
|
|
308
324
|
|
|
325
|
+
### Run the MCP server inside Home Assistant (in-process server entry)
|
|
326
|
+
|
|
327
|
+
The **HA-MCP Custom Component** offers a second config-entry type, **HA-MCP Server**, that runs the **full ha-mcp server in-process**, inside Home Assistant, and exposes it remotely through a Home Assistant webhook. This is a full install method in its own right — useful for **Home Assistant Container / Core** users who can't run add-ons, and available on Home Assistant OS too (it coexists with the add-on on a different default port, `9584` vs `9583`).
|
|
328
|
+
|
|
329
|
+
- **Install:** install **HA-MCP Custom Component** from HACS, or copy `custom_components/ha_mcp_tools` into your `config/custom_components/` directory and restart Home Assistant. Then **Add Integration → HA-MCP Custom Component → HA-MCP Server** and submit — creating the entry starts the server.
|
|
330
|
+
- **Connect URL:** appears in the **HA-MCP Server** notification and on the entry's Configure screen. It's a Home Assistant webhook — `https://<nabu-casa-domain>/api/webhook/<id>` remotely (through Nabu Casa or any reverse proxy) or `http://<home-assistant-host>:8123/api/webhook/<id>` locally. The server is also reachable directly on its own port by default (set **Network access** to `127.0.0.1` to turn direct access off).
|
|
331
|
+
- **Settings panel:** while the server runs, an admin-only **HA-MCP** panel in the Home Assistant sidebar opens its web settings UI (enable/disable/pin tools, feature flags, backups, themes) through Home Assistant itself — no loopback URL needed, the same idea as the add-on's "Open Web UI" button.
|
|
332
|
+
- **Authentication:** by default the secret webhook URL is the credential; optionally require Home Assistant account sign-in (`ha_auth`) for clients that support it.
|
|
333
|
+
- **Release channel:** the entry options let you pick `stable` (the pinned release) or `dev` (the latest development build, refreshed on every reload/restart).
|
|
334
|
+
- **The HA MCP Tools services entry is optional but recommended alongside it** — that second entry type of the same component provides the privileged file/YAML services the server's file tools use, exactly as with the add-on, Docker, and pip installs.
|
|
335
|
+
|
|
336
|
+
[Full in-process server documentation →](docs/in-process-server.md)
|
|
337
|
+
|
|
309
338
|
---
|
|
310
339
|
|
|
311
340
|
## 🧠 Better Results with Agent Skills
|
ha_mcp_dev-7.9.0.dev761/src/ha_mcp_dev.egg-info/PKG-INFO → ha_mcp_dev-7.9.0.dev763/README.md
RENAMED
|
@@ -1,35 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: ha-mcp-dev
|
|
3
|
-
Version: 7.9.0.dev761
|
|
4
|
-
Summary: Home Assistant MCP Server - Complete control of Home Assistant through MCP
|
|
5
|
-
Author-email: Julien <github@qc-h.net>
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/homeassistant-ai/ha-mcp
|
|
8
|
-
Project-URL: Bug Tracker, https://github.com/homeassistant-ai/ha-mcp/issues
|
|
9
|
-
Project-URL: Repository, https://github.com/homeassistant-ai/ha-mcp
|
|
10
|
-
Keywords: mcp,home-assistant,ai,automation,smart-home
|
|
11
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
-
Classifier: Intended Audience :: Developers
|
|
13
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
-
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
-
Classifier: Topic :: Home Automation
|
|
17
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
-
Requires-Python: <3.14,>=3.13
|
|
19
|
-
Description-Content-Type: text/markdown
|
|
20
|
-
License-File: LICENSE
|
|
21
|
-
Requires-Dist: fastmcp==3.4.2
|
|
22
|
-
Requires-Dist: httpx[socks]==0.28.1
|
|
23
|
-
Requires-Dist: pydantic==2.13.4
|
|
24
|
-
Requires-Dist: python-dotenv==1.2.2
|
|
25
|
-
Requires-Dist: truststore==0.10.4
|
|
26
|
-
Requires-Dist: websockets==16.0
|
|
27
|
-
Requires-Dist: cryptography==49.0.0
|
|
28
|
-
Requires-Dist: pydantic-monty==0.0.18
|
|
29
|
-
Requires-Dist: tzdata>=2024.1
|
|
30
|
-
Requires-Dist: packaging>=24.0
|
|
31
|
-
Dynamic: license-file
|
|
32
|
-
|
|
33
1
|
> **Breaking change (v7.3.0):** `ha_config_set_yaml` has been moved to [beta](docs/beta.md).
|
|
34
2
|
|
|
35
3
|
<div align="center">
|
|
@@ -92,6 +60,21 @@ Running Home Assistant OS? Run ha-mcp **inside** Home Assistant — no access to
|
|
|
92
60
|
|
|
93
61
|
> ⚠️ **Two ways to run ha-mcp — don't mix them.** The add-on above is the recommended path. The local **stdio** path below runs ha-mcp on your own computer; it is more advanced, has fewer features, and is behind most connection problems. If you use the add-on, your Claude Desktop config points at the add-on URL through `mcp-proxy` with **no token** — do **not** also keep a local `uvx ha-mcp@latest` entry (with `HOMEASSISTANT_URL` / `HOMEASSISTANT_TOKEN`) in your Claude Desktop config. Running both at once is a known cause of connection hangs.
|
|
94
62
|
|
|
63
|
+
<details>
|
|
64
|
+
<summary><b>Run inside Home Assistant (Container / Core — no add-on)</b></summary>
|
|
65
|
+
|
|
66
|
+
Not on Home Assistant OS? Home Assistant **Container** and **Core** installs can't run add-ons, but the **HA-MCP Custom Component** (`ha_mcp_tools`) can run the **full ha-mcp server in-process**, inside Home Assistant — no separate Docker container, no token to manage. It works on Home Assistant OS too, and coexists with the add-on. The connect URL is a Home Assistant webhook, so it reaches remote MCP clients through **Nabu Casa** (or any reverse proxy) with no extra tunnel.
|
|
67
|
+
|
|
68
|
+
1. Install **HA-MCP Custom Component** from HACS (the same repository you use for ha-mcp), or copy `custom_components/ha_mcp_tools` from this repository into your Home Assistant `config/custom_components/` directory; then restart Home Assistant
|
|
69
|
+
2. Add the integration (**Settings → Devices & Services → Add Integration → HA-MCP Custom Component**) and choose **HA-MCP Server** from the menu, then submit — creating the entry starts the server
|
|
70
|
+
3. Copy the connect URL from the **HA-MCP Server** notification (also shown on the entry's Configure screen)
|
|
71
|
+
4. Manage the server from the **HA-MCP** sidebar panel (admin-only web settings UI)
|
|
72
|
+
5. Connect your AI client to that URL
|
|
73
|
+
|
|
74
|
+
[Full in-process server documentation →](docs/in-process-server.md)
|
|
75
|
+
|
|
76
|
+
</details>
|
|
77
|
+
|
|
95
78
|
### 💻 Run locally with Claude Desktop (stdio · advanced)
|
|
96
79
|
|
|
97
80
|
*No paid subscription required.* This runs ha-mcp on your own machine over stdio.
|
|
@@ -298,7 +281,7 @@ These tools also require feature flags: `HAMCP_ENABLE_FILESYSTEM_TOOLS=true` (fi
|
|
|
298
281
|
|
|
299
282
|
To add manually: open **HACS** > **Integrations** > three-dot menu > **Custom repositories** > add `https://github.com/homeassistant-ai/ha-mcp` (category: Integration) > **Download**.
|
|
300
283
|
|
|
301
|
-
After installing, restart Home Assistant. Then open **Settings** > **Devices & Services** > **Add Integration** and search for **
|
|
284
|
+
After installing, restart Home Assistant. Then open **Settings** > **Devices & Services** > **Add Integration** and search for **HA-MCP Custom Component**.
|
|
302
285
|
|
|
303
286
|
On **Home Assistant OS / Supervised**, the integration offers to add the add-on repository and install and start the **Home Assistant MCP Server** add-on for you — no need to add the add-on repository by hand. On **Container / Core** installs (no Supervisor) there is no add-on; run the server via Docker or pip and the integration just sets up the file/YAML services.
|
|
304
287
|
|
|
@@ -306,6 +289,19 @@ On **Home Assistant OS / Supervised**, the integration offers to add the add-on
|
|
|
306
289
|
|
|
307
290
|
Copy `custom_components/ha_mcp_tools/` from this repository into your HA `config/custom_components/` directory. Restart Home Assistant, then add the integration as described above.
|
|
308
291
|
|
|
292
|
+
### Run the MCP server inside Home Assistant (in-process server entry)
|
|
293
|
+
|
|
294
|
+
The **HA-MCP Custom Component** offers a second config-entry type, **HA-MCP Server**, that runs the **full ha-mcp server in-process**, inside Home Assistant, and exposes it remotely through a Home Assistant webhook. This is a full install method in its own right — useful for **Home Assistant Container / Core** users who can't run add-ons, and available on Home Assistant OS too (it coexists with the add-on on a different default port, `9584` vs `9583`).
|
|
295
|
+
|
|
296
|
+
- **Install:** install **HA-MCP Custom Component** from HACS, or copy `custom_components/ha_mcp_tools` into your `config/custom_components/` directory and restart Home Assistant. Then **Add Integration → HA-MCP Custom Component → HA-MCP Server** and submit — creating the entry starts the server.
|
|
297
|
+
- **Connect URL:** appears in the **HA-MCP Server** notification and on the entry's Configure screen. It's a Home Assistant webhook — `https://<nabu-casa-domain>/api/webhook/<id>` remotely (through Nabu Casa or any reverse proxy) or `http://<home-assistant-host>:8123/api/webhook/<id>` locally. The server is also reachable directly on its own port by default (set **Network access** to `127.0.0.1` to turn direct access off).
|
|
298
|
+
- **Settings panel:** while the server runs, an admin-only **HA-MCP** panel in the Home Assistant sidebar opens its web settings UI (enable/disable/pin tools, feature flags, backups, themes) through Home Assistant itself — no loopback URL needed, the same idea as the add-on's "Open Web UI" button.
|
|
299
|
+
- **Authentication:** by default the secret webhook URL is the credential; optionally require Home Assistant account sign-in (`ha_auth`) for clients that support it.
|
|
300
|
+
- **Release channel:** the entry options let you pick `stable` (the pinned release) or `dev` (the latest development build, refreshed on every reload/restart).
|
|
301
|
+
- **The HA MCP Tools services entry is optional but recommended alongside it** — that second entry type of the same component provides the privileged file/YAML services the server's file tools use, exactly as with the add-on, Docker, and pip installs.
|
|
302
|
+
|
|
303
|
+
[Full in-process server documentation →](docs/in-process-server.md)
|
|
304
|
+
|
|
309
305
|
---
|
|
310
306
|
|
|
311
307
|
## 🧠 Better Results with Agent Skills
|
|
@@ -4,10 +4,13 @@ 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.dev763"
|
|
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
|
|
11
|
+
# interpreter when run in-process by the ha_mcp_tools component (HA
|
|
12
|
+
# core-2026.7 ships Python 3.14). src/ stays 3.13-compatible.
|
|
13
|
+
requires-python = ">=3.13,<3.15"
|
|
11
14
|
license = {text = "MIT"}
|
|
12
15
|
authors = [
|
|
13
16
|
{name = "Julien", email = "github@qc-h.net"}
|
|
@@ -19,6 +22,7 @@ classifiers = [
|
|
|
19
22
|
"License :: OSI Approved :: MIT License",
|
|
20
23
|
"Programming Language :: Python :: 3",
|
|
21
24
|
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Programming Language :: Python :: 3.14",
|
|
22
26
|
"Topic :: Home Automation",
|
|
23
27
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
24
28
|
]
|
|
@@ -30,7 +34,13 @@ dependencies = [
|
|
|
30
34
|
"python-dotenv==1.2.2",
|
|
31
35
|
"truststore==0.10.4",
|
|
32
36
|
"websockets==16.0",
|
|
33
|
-
|
|
37
|
+
# Range (not a hard pin) so the resolver can satisfy Home Assistant's own
|
|
38
|
+
# cryptography constraint when ha-mcp is installed into the HA venv for
|
|
39
|
+
# in-process embedding (HA 2026.6 pins cryptography==48.0.0, 2026.7 pins
|
|
40
|
+
# 48.0.1 — the floor must admit every supported core's pin; live-found on
|
|
41
|
+
# the embedded e2e lane against the HA 2026.6.4 image).
|
|
42
|
+
# cryptography is a transitive dependency only — never imported in src/.
|
|
43
|
+
"cryptography>=48.0.0,<50",
|
|
34
44
|
"pydantic-monty==0.0.18",
|
|
35
45
|
"tzdata>=2024.1",
|
|
36
46
|
"packaging>=24.0",
|
|
@@ -194,6 +204,9 @@ dev = [
|
|
|
194
204
|
"pytest-cov>=5.0.0",
|
|
195
205
|
"pytest-timeout>=2.3.0",
|
|
196
206
|
"pytest-rerunfailures>=15.0",
|
|
207
|
+
# Real voluptuous for the config-flow tests (the component's
|
|
208
|
+
# config_flow imports it; HA provides it at runtime, tests need it here).
|
|
209
|
+
"voluptuous>=0.15.2",
|
|
197
210
|
"pytest-xdist>=3.8.0",
|
|
198
211
|
"requests>=2.25.0",
|
|
199
212
|
"lefthook>=1.10.0",
|
|
@@ -208,6 +221,11 @@ dev = [
|
|
|
208
221
|
version_toml = ["pyproject.toml:project.version"]
|
|
209
222
|
version_variables = [
|
|
210
223
|
"src/ha_mcp/__init__.py:__version__",
|
|
224
|
+
# Keeps the in-process server's stable-channel pin in lockstep with the
|
|
225
|
+
# released server/add-on version - a hand-edited pin would drift the
|
|
226
|
+
# moment a release ships (owner: the server entry must always match the
|
|
227
|
+
# add-on version to avoid confusion).
|
|
228
|
+
"custom_components/ha_mcp_tools/const.py:PINNED_HA_MCP_VERSION",
|
|
211
229
|
]
|
|
212
230
|
build_command = """
|
|
213
231
|
pip install uv && uv lock
|
|
@@ -48,14 +48,34 @@ def is_dev_version(version: str) -> bool:
|
|
|
48
48
|
return ".dev" in version
|
|
49
49
|
|
|
50
50
|
|
|
51
|
+
def is_embedded() -> bool:
|
|
52
|
+
"""Return True when ha-mcp runs in-process inside Home Assistant core.
|
|
53
|
+
|
|
54
|
+
Set to ``1`` by the ha_mcp_tools custom component's in-process server entry
|
|
55
|
+
(``HA_MCP_EMBEDDED``) before the first ``ha_mcp`` import. On HAOS the
|
|
56
|
+
HA core container itself carries ``SUPERVISOR_TOKEN``, so without this flag
|
|
57
|
+
:func:`is_running_in_addon` would report True in-process and route log,
|
|
58
|
+
add-on-management, and settings-persistence logic down the add-on path. The
|
|
59
|
+
embedded server is an ordinary admin client of HA core (loopback URL +
|
|
60
|
+
provisioned token), so those call sites must take their non-add-on branch.
|
|
61
|
+
"""
|
|
62
|
+
return bool(os.environ.get("HA_MCP_EMBEDDED"))
|
|
63
|
+
|
|
64
|
+
|
|
51
65
|
def is_running_in_addon() -> bool:
|
|
52
66
|
"""Return True when running inside a Home Assistant add-on container.
|
|
53
67
|
|
|
54
68
|
The HA Supervisor injects ``SUPERVISOR_TOKEN`` into every add-on's env.
|
|
55
69
|
Checked so the standalone-Docker ``:stable`` banner isn't shown to add-on
|
|
56
70
|
users, who already see the dev/stable distinction in the HAOS add-on UI.
|
|
71
|
+
|
|
72
|
+
Returns False in embedded mode (:func:`is_embedded`) even though the HA core
|
|
73
|
+
container carries ``SUPERVISOR_TOKEN``: the in-process server is a plain
|
|
74
|
+
admin client of HA core, not a Supervisor-managed add-on, so add-on-only
|
|
75
|
+
behavior (Supervisor-direct log fetch, add-on settings routing) must not
|
|
76
|
+
apply.
|
|
57
77
|
"""
|
|
58
|
-
return bool(os.environ.get("SUPERVISOR_TOKEN"))
|
|
78
|
+
return bool(os.environ.get("SUPERVISOR_TOKEN")) and not is_embedded()
|
|
59
79
|
|
|
60
80
|
|
|
61
81
|
def get_supervisor_base_url() -> str:
|
|
@@ -14,7 +14,7 @@ from dotenv import load_dotenv
|
|
|
14
14
|
from pydantic import Field, ValidationInfo, field_validator
|
|
15
15
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
16
16
|
|
|
17
|
-
from ha_mcp._version import get_version
|
|
17
|
+
from ha_mcp._version import get_version, is_running_in_addon
|
|
18
18
|
|
|
19
19
|
logger = logging.getLogger(__name__)
|
|
20
20
|
|
|
@@ -913,7 +913,12 @@ def get_feature_flag_origin(env_name: str) -> str:
|
|
|
913
913
|
)
|
|
914
914
|
is_master = field_name == "enable_beta_features"
|
|
915
915
|
is_beta_sub = field_name in BETA_FEATURE_FIELDS
|
|
916
|
-
|
|
916
|
+
# is_running_in_addon() (not a raw SUPERVISOR_TOKEN read) so the in-process
|
|
917
|
+
# embedded server — which carries SUPERVISOR_TOKEN on HAOS but is not an
|
|
918
|
+
# add-on — is treated as a standalone deployment: its settings-UI edits
|
|
919
|
+
# persist to override files under HA_MCP_CONFIG_DIR instead of being routed
|
|
920
|
+
# to a Supervisor add-on that does not exist.
|
|
921
|
+
in_addon = is_running_in_addon()
|
|
917
922
|
|
|
918
923
|
if in_addon:
|
|
919
924
|
if is_master or is_beta_sub:
|
|
@@ -1034,7 +1039,11 @@ def _apply_feature_flag_overrides(settings: "Settings") -> None:
|
|
|
1034
1039
|
env var still needs to flip the master before the flag takes
|
|
1035
1040
|
effect.
|
|
1036
1041
|
"""
|
|
1037
|
-
|
|
1042
|
+
# is_running_in_addon() (not a raw SUPERVISOR_TOKEN read) so the in-process
|
|
1043
|
+
# embedded server — which carries SUPERVISOR_TOKEN on HAOS but is not an
|
|
1044
|
+
# add-on — applies its settings-UI feature-flag saves like a standalone
|
|
1045
|
+
# deployment instead of short-circuiting here as if start.py owned the env.
|
|
1046
|
+
in_addon = is_running_in_addon()
|
|
1038
1047
|
overrides = _read_feature_flag_override_file()
|
|
1039
1048
|
|
|
1040
1049
|
known = {fname: (ename, ftype) for fname, ename, ftype in FEATURE_FLAG_FIELDS}
|
|
@@ -1268,6 +1277,48 @@ def _apply_advanced_overrides(settings: "Settings") -> None:
|
|
|
1268
1277
|
# Global settings instance
|
|
1269
1278
|
_settings: Settings | None = None
|
|
1270
1279
|
|
|
1280
|
+
# In-process (embedded) HA connection, set only when ha-mcp runs inside Home
|
|
1281
|
+
# Assistant core via the ha_mcp_tools custom component's in-process server entry.
|
|
1282
|
+
# The component hands the loopback URL + a provisioned admin token to ha-mcp
|
|
1283
|
+
# THROUGH THIS DICT — never
|
|
1284
|
+
# via os.environ — so the admin token can't be read from the shared HA process
|
|
1285
|
+
# environment. Applied onto the Settings singleton in ``get_global_settings``.
|
|
1286
|
+
_EMBEDDED_CONNECTION: dict[str, str] = {}
|
|
1287
|
+
|
|
1288
|
+
|
|
1289
|
+
def set_embedded_connection(url: str, token: str) -> None:
|
|
1290
|
+
"""Register the in-process HA connection for embedded mode.
|
|
1291
|
+
|
|
1292
|
+
Embedded-mode only: called by the ha_mcp_tools custom component's in-process
|
|
1293
|
+
server entry inside its server worker thread, before the server is
|
|
1294
|
+
constructed, so the loopback URL
|
|
1295
|
+
and admin token reach ``Settings`` in memory instead of through ``os.environ``.
|
|
1296
|
+
The values survive ``_reset_global_settings()``: the settings-UI reset+rebuild
|
|
1297
|
+
path re-applies them on the next ``get_global_settings()`` call.
|
|
1298
|
+
|
|
1299
|
+
Also applies to an ALREADY-BUILT singleton: importing ``ha_mcp`` runs the
|
|
1300
|
+
package's eager import chain, and ``tools/smart_search/_config.py`` builds
|
|
1301
|
+
the settings singleton at import time (its documented read-once budgets).
|
|
1302
|
+
Registration therefore cannot assume it runs before the first build — the
|
|
1303
|
+
integration imports this function from the very package whose import
|
|
1304
|
+
creates the singleton.
|
|
1305
|
+
"""
|
|
1306
|
+
_EMBEDDED_CONNECTION["url"] = url
|
|
1307
|
+
_EMBEDDED_CONNECTION["token"] = token
|
|
1308
|
+
if _settings is not None:
|
|
1309
|
+
_apply_embedded_connection(_settings)
|
|
1310
|
+
|
|
1311
|
+
|
|
1312
|
+
def _reset_embedded_connection() -> None:
|
|
1313
|
+
"""Drop the registered embedded connection (test seam).
|
|
1314
|
+
|
|
1315
|
+
Sibling to :func:`_reset_global_settings`; lets suites that exercise the
|
|
1316
|
+
in-process token channel isolate state between tests. Not used in production —
|
|
1317
|
+
the connection is registered once per worker thread and is meant to persist.
|
|
1318
|
+
"""
|
|
1319
|
+
_EMBEDDED_CONNECTION.clear()
|
|
1320
|
+
|
|
1321
|
+
|
|
1271
1322
|
# Names of beta sub-flags the master gate has already logged a
|
|
1272
1323
|
# force-False line for in this process. Used to dedup the gate's
|
|
1273
1324
|
# INFO log so we don't spam addon logs on every Settings rebuild
|
|
@@ -1332,7 +1383,11 @@ def get_backup_setting_origin(env_name: str) -> str:
|
|
|
1332
1383
|
- ``"default"``: no env var and no override file entry; the
|
|
1333
1384
|
pydantic field default applies. Web UI edits create the file.
|
|
1334
1385
|
"""
|
|
1335
|
-
|
|
1386
|
+
# is_running_in_addon() rather than a raw SUPERVISOR_TOKEN read so the
|
|
1387
|
+
# embedded in-process server (SUPERVISOR_TOKEN present on HAOS, but not an
|
|
1388
|
+
# add-on) is labeled a standalone deployment and its backup settings persist
|
|
1389
|
+
# to the override file instead of a non-existent Supervisor add-on.
|
|
1390
|
+
if is_running_in_addon():
|
|
1336
1391
|
return "addon"
|
|
1337
1392
|
if os.environ.get(env_name) is not None:
|
|
1338
1393
|
return "env"
|
|
@@ -1424,7 +1479,10 @@ def _apply_backup_overrides(settings: "Settings") -> None:
|
|
|
1424
1479
|
corrupt override file can't push values out of range; out-of-range
|
|
1425
1480
|
or untypable entries are silently skipped.
|
|
1426
1481
|
"""
|
|
1427
|
-
|
|
1482
|
+
# is_running_in_addon() (embedded-aware) so the in-process server applies
|
|
1483
|
+
# its backup override file like a standalone deployment; see the matching
|
|
1484
|
+
# rationale in _apply_feature_flag_overrides / get_backup_setting_origin.
|
|
1485
|
+
if is_running_in_addon():
|
|
1428
1486
|
return
|
|
1429
1487
|
overrides = _read_backup_override_file()
|
|
1430
1488
|
if not overrides:
|
|
@@ -1522,6 +1580,22 @@ def _apply_backup_overrides(settings: "Settings") -> None:
|
|
|
1522
1580
|
continue
|
|
1523
1581
|
|
|
1524
1582
|
|
|
1583
|
+
def _apply_embedded_connection(settings: "Settings") -> None:
|
|
1584
|
+
"""Apply the in-process embedded HA connection (url/token) if registered.
|
|
1585
|
+
|
|
1586
|
+
No-op outside embedded mode. Plain ``setattr`` (``validate_assignment`` is off
|
|
1587
|
+
on ``Settings``, mirroring ``_apply_backup_overrides``), so the loopback URL
|
|
1588
|
+
and admin token are set in memory without ever passing through ``os.environ``.
|
|
1589
|
+
Applied last so it wins over any env/override-file connection values.
|
|
1590
|
+
"""
|
|
1591
|
+
url = _EMBEDDED_CONNECTION.get("url")
|
|
1592
|
+
token = _EMBEDDED_CONNECTION.get("token")
|
|
1593
|
+
if url:
|
|
1594
|
+
settings.homeassistant_url = url.rstrip("/")
|
|
1595
|
+
if token:
|
|
1596
|
+
settings.homeassistant_token = token
|
|
1597
|
+
|
|
1598
|
+
|
|
1525
1599
|
def get_global_settings() -> Settings:
|
|
1526
1600
|
"""Get global settings instance (singleton pattern).
|
|
1527
1601
|
|
|
@@ -1531,6 +1605,10 @@ def get_global_settings() -> Settings:
|
|
|
1531
1605
|
|
|
1532
1606
|
- Feature flags persisted to ``<data_dir>/feature_flags.json``
|
|
1533
1607
|
- Auto-backup settings persisted to ``<data_dir>/backup_settings.json``
|
|
1608
|
+
|
|
1609
|
+
In embedded mode, the in-process HA connection registered via
|
|
1610
|
+
``set_embedded_connection`` is applied last (so a settings-UI reset+rebuild
|
|
1611
|
+
re-picks it up).
|
|
1534
1612
|
"""
|
|
1535
1613
|
global _settings
|
|
1536
1614
|
if _settings is None:
|
|
@@ -1538,9 +1616,22 @@ def get_global_settings() -> Settings:
|
|
|
1538
1616
|
_apply_feature_flag_overrides(_settings)
|
|
1539
1617
|
_apply_backup_overrides(_settings)
|
|
1540
1618
|
_apply_advanced_overrides(_settings)
|
|
1619
|
+
_apply_embedded_connection(_settings)
|
|
1541
1620
|
return _settings
|
|
1542
1621
|
|
|
1543
1622
|
|
|
1623
|
+
def reset_global_settings() -> None:
|
|
1624
|
+
"""Public seam to drop the cached settings singleton.
|
|
1625
|
+
|
|
1626
|
+
The in-process (embedded) server calls this at every start: a config-entry
|
|
1627
|
+
reload reuses the same Python process, so without an explicit reset the
|
|
1628
|
+
singleton built on the FIRST start would keep serving stale feature-flag /
|
|
1629
|
+
override values forever (the add-on gets fresh settings for free from its
|
|
1630
|
+
process restart).
|
|
1631
|
+
"""
|
|
1632
|
+
_reset_global_settings()
|
|
1633
|
+
|
|
1634
|
+
|
|
1544
1635
|
def _reset_global_settings() -> None:
|
|
1545
1636
|
"""Drop the cached settings singleton.
|
|
1546
1637
|
|
|
@@ -156,7 +156,7 @@
|
|
|
156
156
|
main {
|
|
157
157
|
max-width: 1280px;
|
|
158
158
|
margin: 0 auto;
|
|
159
|
-
padding: 0 16px
|
|
159
|
+
padding: 0 16px 40px;
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
/* ── SKIP LINK ───────────────────────────────────────────────────────── */
|
|
@@ -209,7 +209,7 @@
|
|
|
209
209
|
}
|
|
210
210
|
.tab:hover { color: var(--text); background: rgba(255, 255, 255, 0.04); }
|
|
211
211
|
.tab.active { color: var(--accent); border-bottom-color: var(--accent); }
|
|
212
|
-
.panel { display: none; padding-top:
|
|
212
|
+
.panel { display: none; padding-top: 24px; }
|
|
213
213
|
.panel.active { display: block; }
|
|
214
214
|
|
|
215
215
|
/* ── ALERTS (HA ha-alert) ────────────────────────────────────────────── */
|
|
@@ -309,7 +309,7 @@
|
|
|
309
309
|
.group {
|
|
310
310
|
background: var(--surface);
|
|
311
311
|
border-radius: 12px;
|
|
312
|
-
margin-bottom:
|
|
312
|
+
margin-bottom: 12px;
|
|
313
313
|
overflow: hidden;
|
|
314
314
|
border: var(--card-border);
|
|
315
315
|
box-shadow: var(--card-shadow);
|
|
@@ -336,7 +336,7 @@
|
|
|
336
336
|
/* ── INDIVIDUAL TOOLS ────────────────────────────────────────────────── */
|
|
337
337
|
.tool {
|
|
338
338
|
display: flex; align-items: center; justify-content: space-between;
|
|
339
|
-
padding:
|
|
339
|
+
padding: 12px 16px; border-bottom: 1px solid var(--border);
|
|
340
340
|
transition: background 0.1s;
|
|
341
341
|
}
|
|
342
342
|
.tool:last-child { border-bottom: none; }
|
|
@@ -463,18 +463,29 @@
|
|
|
463
463
|
|
|
464
464
|
/* ── SERVER SETTINGS FEATURES ────────────────────────────────────────── */
|
|
465
465
|
.features-sub {
|
|
466
|
-
font-size: 0.
|
|
466
|
+
font-size: 0.8125rem; color: var(--text-secondary);
|
|
467
467
|
margin-bottom: 12px; line-height: 1.5;
|
|
468
468
|
}
|
|
469
|
+
/* Server Settings feature list, grouped into a card (parity with the tool
|
|
470
|
+
groups + accessibility sections) so the tab reads as organized cards
|
|
471
|
+
rather than a flat wall of borderless rows. */
|
|
472
|
+
#featuresBody {
|
|
473
|
+
background: var(--surface);
|
|
474
|
+
border: var(--card-border);
|
|
475
|
+
box-shadow: var(--card-shadow);
|
|
476
|
+
border-radius: 12px;
|
|
477
|
+
padding: 4px 16px;
|
|
478
|
+
margin-bottom: 8px;
|
|
479
|
+
}
|
|
469
480
|
.feature-row {
|
|
470
481
|
display: flex; align-items: flex-start; justify-content: space-between;
|
|
471
|
-
gap: 16px; padding:
|
|
482
|
+
gap: 16px; padding: 16px 0; border-top: 1px solid var(--border);
|
|
472
483
|
}
|
|
473
484
|
.feature-row:first-of-type { border-top: none; }
|
|
474
485
|
.feature-info { flex: 1; min-width: 0; }
|
|
475
486
|
.feature-name { font-size: 0.875rem; font-weight: 500; }
|
|
476
487
|
.feature-help {
|
|
477
|
-
font-size: 0.
|
|
488
|
+
font-size: 0.8125rem; color: var(--text-secondary);
|
|
478
489
|
margin-top: 4px; line-height: 1.5;
|
|
479
490
|
}
|
|
480
491
|
.feature-help code {
|
|
@@ -509,7 +520,7 @@
|
|
|
509
520
|
/* ── ADVANCED SETTINGS ───────────────────────────────────────────────── */
|
|
510
521
|
.adv-section-title {
|
|
511
522
|
font-size: 0.75rem; font-weight: 500; color: var(--text-secondary);
|
|
512
|
-
margin:
|
|
523
|
+
margin: 28px 0 8px; text-transform: uppercase; letter-spacing: 0.08em;
|
|
513
524
|
}
|
|
514
525
|
.adv-section-title.beta-section-title {
|
|
515
526
|
color: var(--warning); font-size: 0.8rem; margin-top: 32px;
|
|
@@ -530,7 +541,17 @@
|
|
|
530
541
|
.adv-save-btn:hover { background: var(--accent-hover); }
|
|
531
542
|
.adv-save-btn:active { filter: brightness(0.9); }
|
|
532
543
|
.adv-save-btn:disabled { opacity: 0.38; cursor: not-allowed; }
|
|
533
|
-
.adv-section {
|
|
544
|
+
.adv-section {
|
|
545
|
+
background: var(--surface);
|
|
546
|
+
border: var(--card-border);
|
|
547
|
+
box-shadow: var(--card-shadow);
|
|
548
|
+
border-radius: 12px;
|
|
549
|
+
padding: 4px 16px;
|
|
550
|
+
margin-bottom: 8px;
|
|
551
|
+
}
|
|
552
|
+
/* A flag-gated section can render with no fields; hide the empty <div> so it
|
|
553
|
+
doesn't show as a hollow card (its overline title above stays as a label). */
|
|
554
|
+
.adv-section:empty { display: none; }
|
|
534
555
|
.adv-section.dimmed { opacity: 0.38; }
|
|
535
556
|
.adv-section.dimmed input,
|
|
536
557
|
.adv-section.dimmed select { cursor: not-allowed; }
|
|
@@ -538,13 +559,13 @@
|
|
|
538
559
|
.adv-section-note { font-size: 0.72rem; color: var(--text-secondary); margin: 4px 0 8px; line-height: 1.5; }
|
|
539
560
|
.adv-row {
|
|
540
561
|
display: flex; align-items: flex-start; justify-content: space-between;
|
|
541
|
-
gap: 16px; padding:
|
|
562
|
+
gap: 16px; padding: 14px 0; border-top: 1px solid var(--border);
|
|
542
563
|
}
|
|
543
564
|
.adv-row:first-child { border-top: none; }
|
|
544
565
|
.adv-row.locked .adv-name { color: var(--text-secondary); }
|
|
545
566
|
.adv-info { flex: 1; min-width: 0; }
|
|
546
567
|
.adv-name { font-size: 0.875rem; font-weight: 500; }
|
|
547
|
-
.adv-help { font-size: 0.
|
|
568
|
+
.adv-help { font-size: 0.8125rem; color: var(--text-secondary); margin-top: 4px; line-height: 1.5; }
|
|
548
569
|
.adv-help code {
|
|
549
570
|
background: var(--code-bg); padding: 1px 5px; border-radius: 3px;
|
|
550
571
|
font-size: 0.7rem; font-family: 'Roboto Mono', 'Courier New', monospace;
|
|
@@ -663,6 +684,11 @@
|
|
|
663
684
|
.diff-hdr { color: var(--diff-hdr); }
|
|
664
685
|
|
|
665
686
|
/* ── TOOL SECURITY POLICIES ──────────────────────────────────────────── */
|
|
687
|
+
/* Bare tab title (no class hook in the markup): give it a defined heading
|
|
688
|
+
size so it doesn't fall back to the oversized browser default h2. */
|
|
689
|
+
#panel-tool-security-policies > h2 {
|
|
690
|
+
font-size: 1.125rem; font-weight: 500; margin-bottom: 8px;
|
|
691
|
+
}
|
|
666
692
|
.policy-rule-card {
|
|
667
693
|
background: var(--surface); border: var(--card-border); border-radius: 12px;
|
|
668
694
|
padding: 14px 16px; margin: 8px 0;
|
|
@@ -721,7 +747,7 @@
|
|
|
721
747
|
padding: 16px; margin-bottom: 12px;
|
|
722
748
|
}
|
|
723
749
|
.a11y-section-title { font-size: 0.9375rem; font-weight: 500; margin-bottom: 4px; }
|
|
724
|
-
.a11y-section-help { font-size: 0.
|
|
750
|
+
.a11y-section-help { font-size: 0.8125rem; color: var(--text-secondary); margin-bottom: 12px; }
|
|
725
751
|
.a11y-options { display: flex; flex-wrap: wrap; gap: 8px; }
|
|
726
752
|
fieldset.a11y-options { border: 0; padding: 0; margin: 0; min-inline-size: auto; }
|
|
727
753
|
.visually-hidden {
|
|
@@ -889,3 +915,41 @@
|
|
|
889
915
|
border-bottom-color: var(--accent);
|
|
890
916
|
box-shadow: inset 0 -1px 0 0 var(--accent);
|
|
891
917
|
}
|
|
918
|
+
|
|
919
|
+
/* ── NARROW SCREENS (phones / small tablets) ─────────────────────────────
|
|
920
|
+
The layout was desktop-only (label left, control right on one line). On a
|
|
921
|
+
phone the label + help + control were crammed into a sliver. Below 640px,
|
|
922
|
+
stack each setting row so the control sits on its own line under the help,
|
|
923
|
+
make text fields fill the width, tighten the page gutters, and drop the
|
|
924
|
+
decorative header icon. Nothing here changes hooks or semantics. */
|
|
925
|
+
@media (max-width: 640px) {
|
|
926
|
+
main { padding: 0 12px 40px; }
|
|
927
|
+
.header { padding: 0 12px; }
|
|
928
|
+
.header-icon { display: none; }
|
|
929
|
+
.header h1 { font-size: 1.0625rem; }
|
|
930
|
+
.panel { padding-top: 20px; }
|
|
931
|
+
|
|
932
|
+
/* One control per line, aligned under its label + help. */
|
|
933
|
+
.feature-row,
|
|
934
|
+
.adv-row {
|
|
935
|
+
flex-direction: column;
|
|
936
|
+
align-items: stretch;
|
|
937
|
+
gap: 10px;
|
|
938
|
+
}
|
|
939
|
+
.feature-control,
|
|
940
|
+
.adv-control { width: 100%; justify-content: flex-start; }
|
|
941
|
+
.adv-control input[type="text"],
|
|
942
|
+
.adv-control select { width: 100%; }
|
|
943
|
+
|
|
944
|
+
/* Backups: stack the row body above its actions; drop the desktop indents. */
|
|
945
|
+
.backup-row { flex-direction: column; align-items: stretch; }
|
|
946
|
+
.backup-row-actions { flex-wrap: wrap; }
|
|
947
|
+
.backup-field-label { min-width: 0; }
|
|
948
|
+
.backup-field-help { margin-left: 0; }
|
|
949
|
+
|
|
950
|
+
/* Policy predicate form: the desktop hint indent has no room here. */
|
|
951
|
+
.policy-predicate-form .policy-form-hint { padding-left: 0; }
|
|
952
|
+
|
|
953
|
+
/* Modal fills the small viewport instead of floating in a sliver. */
|
|
954
|
+
.modal { max-height: 94vh; }
|
|
955
|
+
}
|