Files
homeai/homeai-agent/custom_components/openclaw_conversation/__init__.py
Aodhan Collins 6db8ae4492 feat: complete voice pipeline — fix wake word crash, bridge timeout, HA conversation agent
- Fix Wyoming satellite crash on wake word: convert macOS .aiff chimes to .wav
  (Python wave module only reads RIFF format, not AIFF)
- Fix OpenClaw HTTP bridge: increase subprocess timeout 30s → 120s, add SO_REUSEADDR
- Fix HA conversation component: use HTTP agent (not CLI) since HA runs in Docker
  on a different machine; update default host to Mac Mini IP, timeout to 120s
- Rewrite character manager as Vite+React app with schema validation
- Add Wyoming satellite wake word command, ElevenLabs TTS server, wakeword monitor
- Add Phase 5 development plan
- Update TODO.md: mark voice pipeline and agent tasks complete

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 00:15:55 +00:00

99 lines
2.7 KiB
Python

"""OpenClaw Conversation integration for Home Assistant."""
from __future__ import annotations
import logging
from typing import Any
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from .const import (
CONF_AGENT_NAME,
CONF_OPENCLAW_HOST,
CONF_OPENCLAW_PORT,
CONF_TIMEOUT,
DEFAULT_AGENT,
DEFAULT_HOST,
DEFAULT_PORT,
DEFAULT_TIMEOUT,
DOMAIN,
)
from .conversation import OpenClawAgent
_LOGGER = logging.getLogger(__name__)
PLATFORMS = [Platform.CONVERSATION]
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Optional(CONF_OPENCLAW_HOST, default=DEFAULT_HOST): cv.string,
vol.Optional(CONF_OPENCLAW_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_AGENT_NAME, default=DEFAULT_AGENT): cv.string,
vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
}
)
},
extra=vol.ALLOW_EXTRA,
)
async def async_setup(hass: HomeAssistant, config: dict[str, Any]) -> bool:
"""Set up the OpenClaw Conversation component."""
hass.data.setdefault(DOMAIN, {})
if DOMAIN not in config:
return True
conf = config[DOMAIN]
# Store config
hass.data[DOMAIN] = {
"config": conf,
}
# Register the conversation agent (HTTP-based for cross-network access)
agent = OpenClawAgent(hass, conf)
# Add to conversation agent registry
from homeassistant.components import conversation
conversation.async_set_agent(hass, DOMAIN, agent)
_LOGGER.info("OpenClaw Conversation agent registered")
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up OpenClaw Conversation from a config entry."""
hass.data.setdefault(DOMAIN, {})
# Store entry data
hass.data[DOMAIN][entry.entry_id] = entry.data
# Register the conversation agent (HTTP-based for cross-network access)
agent = OpenClawAgent(hass, entry.data)
from homeassistant.components import conversation
conversation.async_set_agent(hass, entry, agent)
_LOGGER.info("OpenClaw Conversation agent registered from config entry")
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
# Unregister the conversation agent
from homeassistant.components import conversation
conversation.async_unset_agent(hass, entry)
hass.data[DOMAIN].pop(entry.entry_id, None)
return True