"""The Remote Media Player integration.""" 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, ServiceCall from homeassistant.helpers import config_validation as cv from .api_client import MediaServerClient, MediaServerError from .const import ( ATTR_FILE_PATH, ATTR_SCRIPT_ARGS, ATTR_SCRIPT_NAME, CONF_HOST, CONF_PORT, CONF_TOKEN, DOMAIN, SERVICE_EXECUTE_SCRIPT, SERVICE_PLAY_MEDIA_FILE, ) _LOGGER = logging.getLogger(__name__) PLATFORMS: list[Platform] = [Platform.MEDIA_PLAYER, Platform.BUTTON] # Service schema for execute_script SERVICE_EXECUTE_SCRIPT_SCHEMA = vol.Schema( { vol.Required(ATTR_SCRIPT_NAME): cv.string, vol.Optional(ATTR_SCRIPT_ARGS, default=[]): vol.All( cv.ensure_list, [cv.string] ), } ) # Service schema for play_media_file SERVICE_PLAY_MEDIA_FILE_SCHEMA = vol.Schema( { vol.Required(ATTR_FILE_PATH): cv.string, } ) async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Remote Media Player from a config entry. Args: hass: Home Assistant instance entry: Config entry Returns: True if setup was successful """ _LOGGER.debug("Setting up Remote Media Player: %s", entry.entry_id) # Create API client client = MediaServerClient( host=entry.data[CONF_HOST], port=entry.data[CONF_PORT], token=entry.data[CONF_TOKEN], ) # Verify connection if not await client.check_connection(): _LOGGER.error("Failed to connect to Media Server") await client.close() return False # Store client in hass.data hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = { "client": client, } # Register services if not already registered if not hass.services.has_service(DOMAIN, SERVICE_EXECUTE_SCRIPT): async def async_execute_script(call: ServiceCall) -> dict[str, Any]: """Execute a script on the media server.""" script_name = call.data[ATTR_SCRIPT_NAME] script_args = call.data.get(ATTR_SCRIPT_ARGS, []) _LOGGER.debug( "Executing script '%s' with args: %s", script_name, script_args ) # Get all clients and execute on all of them results = {} for entry_id, data in hass.data[DOMAIN].items(): client: MediaServerClient = data["client"] try: result = await client.execute_script(script_name, script_args) results[entry_id] = result _LOGGER.info( "Script '%s' executed on %s: success=%s", script_name, entry_id, result.get("success", False), ) except MediaServerError as err: _LOGGER.error( "Failed to execute script '%s' on %s: %s", script_name, entry_id, err, ) results[entry_id] = {"success": False, "error": str(err)} return results hass.services.async_register( DOMAIN, SERVICE_EXECUTE_SCRIPT, async_execute_script, schema=SERVICE_EXECUTE_SCRIPT_SCHEMA, ) # Register play_media_file service if not already registered if not hass.services.has_service(DOMAIN, SERVICE_PLAY_MEDIA_FILE): async def async_play_media_file(call: ServiceCall) -> None: """Handle play_media_file service call.""" file_path = call.data[ATTR_FILE_PATH] _LOGGER.debug("Service play_media_file called with path: %s", file_path) # Execute on all configured media server instances for entry_id, data in hass.data[DOMAIN].items(): client: MediaServerClient = data["client"] try: await client.play_media_file(file_path) _LOGGER.info("Started playback of %s on %s", file_path, entry_id) except MediaServerError as err: _LOGGER.error("Failed to play %s on %s: %s", file_path, entry_id, err) hass.services.async_register( DOMAIN, SERVICE_PLAY_MEDIA_FILE, async_play_media_file, schema=SERVICE_PLAY_MEDIA_FILE_SCHEMA, ) # Forward setup to platforms await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) # Register update listener for options entry.async_on_unload(entry.add_update_listener(async_update_options)) return True async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry. Args: hass: Home Assistant instance entry: Config entry Returns: True if unload was successful """ _LOGGER.debug("Unloading Remote Media Player: %s", entry.entry_id) # Unload platforms unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) if unload_ok: # Close client and remove data data = hass.data[DOMAIN].pop(entry.entry_id) # Shutdown coordinator (WebSocket cleanup) if "coordinator" in data: await data["coordinator"].async_shutdown() # Close HTTP client await data["client"].close() # Remove services if this was the last entry if not hass.data[DOMAIN]: hass.services.async_remove(DOMAIN, SERVICE_EXECUTE_SCRIPT) hass.services.async_remove(DOMAIN, SERVICE_PLAY_MEDIA_FILE) return unload_ok async def async_update_options(hass: HomeAssistant, entry: ConfigEntry) -> None: """Handle options update. Args: hass: Home Assistant instance entry: Config entry """ _LOGGER.debug("Options updated for: %s", entry.entry_id) await hass.config_entries.async_reload(entry.entry_id)