configurable speaker based on device_id

This commit is contained in:
Pierre Wessman 2025-11-07 13:18:10 +01:00
parent b6c0d6d752
commit 5dec833544
4 changed files with 34 additions and 9 deletions

View File

@ -20,7 +20,8 @@ class Agent:
history: list = None, history: list = None,
tools: Union[None, list] = None, tools: Union[None, list] = None,
prompts: Union[None, list] = None, prompts: Union[None, list] = None,
language: str = "en" language: str = "en",
device_id: str = ""
): ):
""" """
Ask the agent a question. Ask the agent a question.

View File

@ -23,6 +23,7 @@ with open("config.yml", "r", encoding="utf8") as file:
def load_plugins(config): def load_plugins(config):
tools = [] tools = []
prompts = [] prompts = []
plugin_instances = []
for plugin_folder in os.listdir("./" + PLUGINS_FOLDER): for plugin_folder in os.listdir("./" + PLUGINS_FOLDER):
if ( if (
@ -33,15 +34,16 @@ def load_plugins(config):
module_path = f"{PLUGINS_FOLDER}.{plugin_folder}.plugin" module_path = f"{PLUGINS_FOLDER}.{plugin_folder}.plugin"
module = importlib.import_module(module_path) module = importlib.import_module(module_path)
plugin = module.Plugin(config) plugin = module.Plugin(config)
plugin_instances.append(plugin)
tools += plugin.tools() tools += plugin.tools()
prompt = plugin.prompt() prompt = plugin.prompt()
if prompt is not None: if prompt is not None:
prompts.append(prompt) prompts.append(prompt)
return tools, prompts return tools, prompts, plugin_instances
tools, prompts = load_plugins(config) tools, prompts, plugins = load_plugins(config)
app = FastAPI() app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static") app.mount("/static", StaticFiles(directory="static"), name="static")
@ -89,5 +91,9 @@ def ask(q: Question):
if q.history: if q.history:
history = [q.model_dump() for q in q.history] history = [q.model_dump() for q in q.history]
answer = agent.ask(question=q.question, history=history, tools=tools, prompts=prompts, language=q.language) # Set device_id on all plugin instances
for plugin in plugins:
plugin.device_id = q.device_id
answer = agent.ask(question=q.question, history=history, tools=tools, prompts=prompts, language=q.language, device_id=q.device_id)
return {"question": q.question, "answer": answer, "history": q.history} return {"question": q.question, "answer": answer, "history": q.history}

View File

@ -7,6 +7,7 @@ class BasePlugin:
def __init__(self, config: dict) -> None: def __init__(self, config: dict) -> None:
self.config = config self.config = config
self.homeassistant = HomeAssistant(config) self.homeassistant = HomeAssistant(config)
self.device_id = ""
def prompt(self) -> str | None: def prompt(self) -> str | None:
return return

View File

@ -15,17 +15,32 @@ class PlayMusicInput(BaseModel):
class Plugin(BasePlugin): class Plugin(BasePlugin):
def __init__(self, config: dict) -> None: def __init__(self, config: dict) -> None:
super().__init__(config=config) super().__init__(config=config)
self.entity_id = self.config["plugins"]["music"]["default_speaker"]
self.spotify = spotipy.Spotify( self.spotify = spotipy.Spotify(
auth_manager=SpotifyOAuth( auth_manager=SpotifyOAuth(
scope="user-library-read", scope="user-library-read",
redirect_uri="http://localhost:8080", redirect_uri="http://localhost:8080",
client_id=self.config["plugins"]["music"]["spotify_client_id"], client_id=self.config["plugins"]["music"]["spotify_client_id"],
client_secret=self.config["plugins"]["music"]["spotify_client_secret"] client_secret=self.config["plugins"]["music"]["spotify_client_secret"]
) )
) )
def _get_speaker_for_device(self, device_id: str) -> str:
"""
Get the appropriate speaker entity_id based on device_id.
Falls back to default_speaker if device_id is not found or empty.
"""
device_speakers = self.config["plugins"]["music"].get("device_speakers", {})
if device_id and device_id in device_speakers:
speaker = device_speakers[device_id]
logging.info(f"Using device-specific speaker for {device_id}: {speaker}")
return speaker
default_speaker = self.config["plugins"]["music"]["default_speaker"]
logging.info(f"Using default speaker: {default_speaker}")
return default_speaker
def _search(self, query: str, limit: int = 10): def _search(self, query: str, limit: int = 10):
_result = self.spotify.search(query, limit=limit) _result = self.spotify.search(query, limit=limit)
result = [] result = []
@ -45,9 +60,10 @@ class Plugin(BasePlugin):
Play music using a search query. Play music using a search query.
""" """
track = self._search(input.query, limit=1)[0] track = self._search(input.query, limit=1)[0]
logging.info(f"Playing {track['name']} by {', '.join(track['artists'])}") speaker = self._get_speaker_for_device(self.device_id)
logging.info(f"Playing {track['name']} by {', '.join(track['artists'])} on {speaker}")
payload = { payload = {
"entity_id": self.entity_id, "entity_id": speaker,
"media_content_id": track["uri"], "media_content_id": track["uri"],
"media_content_type": "music", "media_content_type": "music",
"enqueue": "play", "enqueue": "play",
@ -59,7 +75,8 @@ class Plugin(BasePlugin):
""" """
Stop playback of music. Stop playback of music.
""" """
speaker = self._get_speaker_for_device(self.device_id)
self.homeassistant.call_api( self.homeassistant.call_api(
f"services/media_player/media_pause", payload={"entity_id": self.entity_id} f"services/media_player/media_pause", payload={"entity_id": speaker}
) )
return json.dumps({"status": "success", "message": f"Music paused."}) return json.dumps({"status": "success", "message": f"Music paused."})