Add config flow to kodi (#38551)
* Add config flow to kodi * Fix lint errors * Remove entry update listener * Create test_init.py * Apply suggestions from code review Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update __init__.py * fix indentation * Apply suggestions from code review * Apply suggestions from code review * Update tests/components/kodi/__init__.py * Fix init test * Fix merge * More review changes * Apply suggestions from code review * Apply suggestions from code review Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Fix black formatting * Fix Flake8 * Don't store CONF_ID * Fall back to entry id * Apply suggestions from code review Co-authored-by: J. Nick Koston <nick@koston.org> * Update __init__.py * Apply suggestions from code review * Apply suggestions from code review Co-authored-by: Chris Talkington <chris@talkingtontech.com> Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
e0e31693f5
commit
c1ed584f2d
18 changed files with 1362 additions and 527 deletions
|
@ -1,89 +1,100 @@
|
|||
"""The kodi component."""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
from pykodi import CannotConnectError, InvalidAuthError, Kodi, get_kodi_connection
|
||||
|
||||
from homeassistant.components.kodi.const import DOMAIN
|
||||
from homeassistant.components.media_player.const import DOMAIN as MP_DOMAIN
|
||||
from homeassistant.const import ATTR_ENTITY_ID, CONF_PLATFORM
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
|
||||
SERVICE_ADD_MEDIA = "add_to_playlist"
|
||||
SERVICE_CALL_METHOD = "call_method"
|
||||
|
||||
ATTR_MEDIA_TYPE = "media_type"
|
||||
ATTR_MEDIA_NAME = "media_name"
|
||||
ATTR_MEDIA_ARTIST_NAME = "artist_name"
|
||||
ATTR_MEDIA_ID = "media_id"
|
||||
ATTR_METHOD = "method"
|
||||
|
||||
MEDIA_PLAYER_SCHEMA = vol.Schema({ATTR_ENTITY_ID: cv.comp_entity_ids})
|
||||
|
||||
KODI_ADD_MEDIA_SCHEMA = MEDIA_PLAYER_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(ATTR_MEDIA_TYPE): cv.string,
|
||||
vol.Optional(ATTR_MEDIA_ID): cv.string,
|
||||
vol.Optional(ATTR_MEDIA_NAME): cv.string,
|
||||
vol.Optional(ATTR_MEDIA_ARTIST_NAME): cv.string,
|
||||
}
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_HOST,
|
||||
CONF_PASSWORD,
|
||||
CONF_PORT,
|
||||
CONF_SSL,
|
||||
CONF_USERNAME,
|
||||
EVENT_HOMEASSISTANT_STOP,
|
||||
)
|
||||
KODI_CALL_METHOD_SCHEMA = MEDIA_PLAYER_SCHEMA.extend(
|
||||
{vol.Required(ATTR_METHOD): cv.string}, extra=vol.ALLOW_EXTRA
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import (
|
||||
CONF_WS_PORT,
|
||||
DATA_CONNECTION,
|
||||
DATA_KODI,
|
||||
DATA_REMOVE_LISTENER,
|
||||
DATA_VERSION,
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
SERVICE_TO_METHOD = {
|
||||
SERVICE_ADD_MEDIA: {
|
||||
"method": "async_add_media_to_playlist",
|
||||
"schema": KODI_ADD_MEDIA_SCHEMA,
|
||||
},
|
||||
SERVICE_CALL_METHOD: {
|
||||
"method": "async_call_method",
|
||||
"schema": KODI_CALL_METHOD_SCHEMA,
|
||||
},
|
||||
}
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
PLATFORMS = ["media_player"]
|
||||
|
||||
|
||||
async def async_setup(hass, config):
|
||||
"""Set up the Kodi integration."""
|
||||
if any((CONF_PLATFORM, DOMAIN) in cfg.items() for cfg in config.get(MP_DOMAIN, [])):
|
||||
# Register the Kodi media_player services
|
||||
async def async_service_handler(service):
|
||||
"""Map services to methods on MediaPlayerEntity."""
|
||||
method = SERVICE_TO_METHOD.get(service.service)
|
||||
if not method:
|
||||
return
|
||||
|
||||
params = {
|
||||
key: value for key, value in service.data.items() if key != "entity_id"
|
||||
}
|
||||
entity_ids = service.data.get("entity_id")
|
||||
if entity_ids:
|
||||
target_players = [
|
||||
player
|
||||
for player in hass.data[DOMAIN].values()
|
||||
if player.entity_id in entity_ids
|
||||
]
|
||||
else:
|
||||
target_players = hass.data[DOMAIN].values()
|
||||
|
||||
update_tasks = []
|
||||
for player in target_players:
|
||||
await getattr(player, method["method"])(**params)
|
||||
|
||||
for player in target_players:
|
||||
if player.should_poll:
|
||||
update_coro = player.async_update_ha_state(True)
|
||||
update_tasks.append(update_coro)
|
||||
|
||||
if update_tasks:
|
||||
await asyncio.wait(update_tasks)
|
||||
|
||||
for service in SERVICE_TO_METHOD:
|
||||
schema = SERVICE_TO_METHOD[service]["schema"]
|
||||
hass.services.async_register(
|
||||
DOMAIN, service, async_service_handler, schema=schema
|
||||
)
|
||||
|
||||
# Return boolean to indicate that initialization was successful.
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Set up Kodi from a config entry."""
|
||||
conn = get_kodi_connection(
|
||||
entry.data[CONF_HOST],
|
||||
entry.data[CONF_PORT],
|
||||
entry.data[CONF_WS_PORT],
|
||||
entry.data[CONF_USERNAME],
|
||||
entry.data[CONF_PASSWORD],
|
||||
entry.data[CONF_SSL],
|
||||
session=async_get_clientsession(hass),
|
||||
)
|
||||
try:
|
||||
await conn.connect()
|
||||
kodi = Kodi(conn)
|
||||
await kodi.ping()
|
||||
raw_version = (await kodi.get_application_properties(["version"]))["version"]
|
||||
except CannotConnectError as error:
|
||||
raise ConfigEntryNotReady from error
|
||||
except InvalidAuthError as error:
|
||||
_LOGGER.error(
|
||||
"Login to %s failed: [%s]", entry.data[CONF_HOST], error,
|
||||
)
|
||||
return False
|
||||
|
||||
async def _close(event):
|
||||
await conn.close()
|
||||
|
||||
remove_stop_listener = hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _close)
|
||||
|
||||
version = f"{raw_version['major']}.{raw_version['minor']}"
|
||||
hass.data[DOMAIN][entry.entry_id] = {
|
||||
DATA_CONNECTION: conn,
|
||||
DATA_KODI: kodi,
|
||||
DATA_REMOVE_LISTENER: remove_stop_listener,
|
||||
DATA_VERSION: version,
|
||||
}
|
||||
|
||||
for component in PLATFORMS:
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setup(entry, component)
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Unload a config entry."""
|
||||
unload_ok = all(
|
||||
await asyncio.gather(
|
||||
*[
|
||||
hass.config_entries.async_forward_entry_unload(entry, component)
|
||||
for component in PLATFORMS
|
||||
]
|
||||
)
|
||||
)
|
||||
if unload_ok:
|
||||
data = hass.data[DOMAIN].pop(entry.entry_id)
|
||||
await data[DATA_CONNECTION].close()
|
||||
data[DATA_REMOVE_LISTENER]()
|
||||
|
||||
return unload_ok
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue