Files
haos-hacs-integration-media…/haos-integration/custom_components/remote_media_player/__init__.py
alexei.dolgolyov e26df64e4b Refactor project into two standalone components
Split monorepo into separate units for future independent repositories:
- media-server/: Standalone FastAPI server with own README, requirements,
  config example, and CLAUDE.md
- haos-integration/: HACS-ready Home Assistant integration with hacs.json,
  own README, and CLAUDE.md

Both components now have their own .gitignore files and can be easily
extracted into separate repositories.

Also adds custom icon support for scripts configuration.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 14:36:23 +03:00

165 lines
4.8 KiB
Python

"""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_SCRIPT_ARGS,
ATTR_SCRIPT_NAME,
CONF_HOST,
CONF_PORT,
CONF_TOKEN,
DOMAIN,
SERVICE_EXECUTE_SCRIPT,
)
_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]
),
}
)
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,
)
# 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)
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)