mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Move Kodi services from 'media_player' domain to 'kodi' (#25753)
* Create const.py * Register services to 'kodi' domain, not 'media_player' * Add const.py to .coveragerc * 'DATA_KODI' -> 'DOMAIN' * Move the Kodi services descriptions to the Kodi component * Register Kodi services in __init__.py * Finish registering Kodi services in __init__.py * Remove logging statement intended only for testing * Combine homeassistant.const imports * Add __init__.py to .coveragerc
This commit is contained in:
parent
ff92307d65
commit
68ee828674
@ -317,6 +317,8 @@ omit =
|
|||||||
homeassistant/components/knx/*
|
homeassistant/components/knx/*
|
||||||
homeassistant/components/knx/climate.py
|
homeassistant/components/knx/climate.py
|
||||||
homeassistant/components/knx/cover.py
|
homeassistant/components/knx/cover.py
|
||||||
|
homeassistant/components/kodi/__init__.py
|
||||||
|
homeassistant/components/kodi/const.py
|
||||||
homeassistant/components/kodi/media_player.py
|
homeassistant/components/kodi/media_player.py
|
||||||
homeassistant/components/kodi/notify.py
|
homeassistant/components/kodi/notify.py
|
||||||
homeassistant/components/konnected/*
|
homeassistant/components/konnected/*
|
||||||
|
@ -1 +1,91 @@
|
|||||||
"""The kodi component."""
|
"""The kodi component."""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.const import ATTR_ENTITY_ID, CONF_PLATFORM
|
||||||
|
from homeassistant.helpers import config_validation as cv
|
||||||
|
from homeassistant.components.kodi.const import DOMAIN
|
||||||
|
from homeassistant.components.media_player.const import DOMAIN as MP_DOMAIN
|
||||||
|
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
KODI_CALL_METHOD_SCHEMA = MEDIA_PLAYER_SCHEMA.extend(
|
||||||
|
{vol.Required(ATTR_METHOD): cv.string}, extra=vol.ALLOW_EXTRA
|
||||||
|
)
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 MediaPlayerDevice."""
|
||||||
|
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.
|
||||||
|
return True
|
||||||
|
2
homeassistant/components/kodi/const.py
Normal file
2
homeassistant/components/kodi/const.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
"""Constants for the Kodi platform."""
|
||||||
|
DOMAIN = "kodi"
|
@ -1,5 +1,4 @@
|
|||||||
"""Support for interfacing with the XBMC/Kodi JSON-RPC API."""
|
"""Support for interfacing with the XBMC/Kodi JSON-RPC API."""
|
||||||
import asyncio
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
import logging
|
import logging
|
||||||
@ -10,9 +9,10 @@ import urllib
|
|||||||
import aiohttp
|
import aiohttp
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.components.kodi import SERVICE_CALL_METHOD
|
||||||
|
from homeassistant.components.kodi.const import DOMAIN
|
||||||
from homeassistant.components.media_player import MediaPlayerDevice, PLATFORM_SCHEMA
|
from homeassistant.components.media_player import MediaPlayerDevice, PLATFORM_SCHEMA
|
||||||
from homeassistant.components.media_player.const import (
|
from homeassistant.components.media_player.const import (
|
||||||
DOMAIN,
|
|
||||||
MEDIA_TYPE_CHANNEL,
|
MEDIA_TYPE_CHANNEL,
|
||||||
MEDIA_TYPE_MOVIE,
|
MEDIA_TYPE_MOVIE,
|
||||||
MEDIA_TYPE_MUSIC,
|
MEDIA_TYPE_MUSIC,
|
||||||
@ -34,7 +34,6 @@ from homeassistant.components.media_player.const import (
|
|||||||
SUPPORT_VOLUME_STEP,
|
SUPPORT_VOLUME_STEP,
|
||||||
)
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID,
|
|
||||||
CONF_HOST,
|
CONF_HOST,
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
CONF_PASSWORD,
|
CONF_PASSWORD,
|
||||||
@ -134,42 +133,6 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
SERVICE_ADD_MEDIA = "kodi_add_to_playlist"
|
|
||||||
SERVICE_CALL_METHOD = "kodi_call_method"
|
|
||||||
|
|
||||||
DATA_KODI = "kodi"
|
|
||||||
|
|
||||||
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})
|
|
||||||
|
|
||||||
MEDIA_PLAYER_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,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
MEDIA_PLAYER_CALL_METHOD_SCHEMA = MEDIA_PLAYER_SCHEMA.extend(
|
|
||||||
{vol.Required(ATTR_METHOD): cv.string}, extra=vol.ALLOW_EXTRA
|
|
||||||
)
|
|
||||||
|
|
||||||
SERVICE_TO_METHOD = {
|
|
||||||
SERVICE_ADD_MEDIA: {
|
|
||||||
"method": "async_add_media_to_playlist",
|
|
||||||
"schema": MEDIA_PLAYER_ADD_MEDIA_SCHEMA,
|
|
||||||
},
|
|
||||||
SERVICE_CALL_METHOD: {
|
|
||||||
"method": "async_call_method",
|
|
||||||
"schema": MEDIA_PLAYER_CALL_METHOD_SCHEMA,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def _check_deprecated_turn_off(hass, turn_off_action):
|
def _check_deprecated_turn_off(hass, turn_off_action):
|
||||||
"""Create an equivalent script for old turn off actions."""
|
"""Create an equivalent script for old turn off actions."""
|
||||||
@ -205,8 +168,8 @@ def _check_deprecated_turn_off(hass, turn_off_action):
|
|||||||
|
|
||||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||||
"""Set up the Kodi platform."""
|
"""Set up the Kodi platform."""
|
||||||
if DATA_KODI not in hass.data:
|
if DOMAIN not in hass.data:
|
||||||
hass.data[DATA_KODI] = dict()
|
hass.data[DOMAIN] = dict()
|
||||||
|
|
||||||
unique_id = None
|
unique_id = None
|
||||||
# Is this a manual configuration?
|
# Is this a manual configuration?
|
||||||
@ -231,14 +194,14 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
|||||||
# Only add a device once, so discovered devices do not override manual
|
# Only add a device once, so discovered devices do not override manual
|
||||||
# config.
|
# config.
|
||||||
ip_addr = socket.gethostbyname(host)
|
ip_addr = socket.gethostbyname(host)
|
||||||
if ip_addr in hass.data[DATA_KODI]:
|
if ip_addr in hass.data[DOMAIN]:
|
||||||
return
|
return
|
||||||
|
|
||||||
# If we got an unique id, check that it does not exist already.
|
# If we got an unique id, check that it does not exist already.
|
||||||
# This is necessary as netdisco does not deterministally return the same
|
# This is necessary as netdisco does not deterministally return the same
|
||||||
# advertisement when the service is offered over multiple IP addresses.
|
# advertisement when the service is offered over multiple IP addresses.
|
||||||
if unique_id is not None:
|
if unique_id is not None:
|
||||||
for device in hass.data[DATA_KODI].values():
|
for device in hass.data[DOMAIN].values():
|
||||||
if device.unique_id == unique_id:
|
if device.unique_id == unique_id:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -258,49 +221,9 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
|||||||
unique_id=unique_id,
|
unique_id=unique_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
hass.data[DATA_KODI][ip_addr] = entity
|
hass.data[DOMAIN][ip_addr] = entity
|
||||||
async_add_entities([entity], update_before_add=True)
|
async_add_entities([entity], update_before_add=True)
|
||||||
|
|
||||||
async def async_service_handler(service):
|
|
||||||
"""Map services to methods on MediaPlayerDevice."""
|
|
||||||
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[DATA_KODI].values()
|
|
||||||
if player.entity_id in entity_ids
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
target_players = hass.data[DATA_KODI].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)
|
|
||||||
|
|
||||||
if hass.services.has_service(DOMAIN, SERVICE_ADD_MEDIA):
|
|
||||||
return
|
|
||||||
|
|
||||||
for service in SERVICE_TO_METHOD:
|
|
||||||
schema = SERVICE_TO_METHOD[service]["schema"]
|
|
||||||
hass.services.async_register(
|
|
||||||
DOMAIN, service, async_service_handler, schema=schema
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def cmd(func):
|
def cmd(func):
|
||||||
"""Catch command exceptions."""
|
"""Catch command exceptions."""
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
# Describes the format for available Kodi services
|
||||||
|
|
||||||
|
add_to_playlist:
|
||||||
|
description: Add music to the default playlist (i.e. playlistid=0).
|
||||||
|
fields:
|
||||||
|
entity_id:
|
||||||
|
description: Name(s) of the Kodi entities where to add the media.
|
||||||
|
example: 'media_player.living_room_kodi'
|
||||||
|
media_type:
|
||||||
|
description: Media type identifier. It must be one of SONG or ALBUM.
|
||||||
|
example: ALBUM
|
||||||
|
media_id:
|
||||||
|
description: Unique Id of the media entry to add (`songid` or albumid`). If not defined, `media_name` and `artist_name` are needed to search the Kodi music library.
|
||||||
|
example: 123456
|
||||||
|
media_name:
|
||||||
|
description: Optional media name for filtering media. Can be 'ALL' when `media_type` is 'ALBUM' and `artist_name` is specified, to add all songs from one artist.
|
||||||
|
example: 'Highway to Hell'
|
||||||
|
artist_name:
|
||||||
|
description: Optional artist name for filtering media.
|
||||||
|
example: 'AC/DC'
|
||||||
|
|
||||||
|
call_method:
|
||||||
|
description: 'Call a Kodi JSONRPC API method with optional parameters. Results of the Kodi API call will be redirected in a Home Assistant event: `kodi_call_method_result`.'
|
||||||
|
fields:
|
||||||
|
entity_id:
|
||||||
|
description: Name(s) of the Kodi entities where to run the API method.
|
||||||
|
example: 'media_player.living_room_kodi'
|
||||||
|
method:
|
||||||
|
description: Name of the Kodi JSONRPC API method to be called.
|
||||||
|
example: 'VideoLibrary.GetRecentlyAddedEpisodes'
|
@ -232,35 +232,6 @@ soundtouch_remove_zone_slave:
|
|||||||
description: Name of slaves entities to remove from the existing zone.
|
description: Name of slaves entities to remove from the existing zone.
|
||||||
example: 'media_player.soundtouch_bedroom'
|
example: 'media_player.soundtouch_bedroom'
|
||||||
|
|
||||||
kodi_add_to_playlist:
|
|
||||||
description: Add music to the default playlist (i.e. playlistid=0).
|
|
||||||
fields:
|
|
||||||
entity_id:
|
|
||||||
description: Name(s) of the Kodi entities where to add the media.
|
|
||||||
example: 'media_player.living_room_kodi'
|
|
||||||
media_type:
|
|
||||||
description: Media type identifier. It must be one of SONG or ALBUM.
|
|
||||||
example: ALBUM
|
|
||||||
media_id:
|
|
||||||
description: Unique Id of the media entry to add (`songid` or albumid`). If not defined, `media_name` and `artist_name` are needed to search the Kodi music library.
|
|
||||||
example: 123456
|
|
||||||
media_name:
|
|
||||||
description: Optional media name for filtering media. Can be 'ALL' when `media_type` is 'ALBUM' and `artist_name` is specified, to add all songs from one artist.
|
|
||||||
example: 'Highway to Hell'
|
|
||||||
artist_name:
|
|
||||||
description: Optional artist name for filtering media.
|
|
||||||
example: 'AC/DC'
|
|
||||||
|
|
||||||
kodi_call_method:
|
|
||||||
description: 'Call a Kodi JSONRPC API method with optional parameters. Results of the Kodi API call will be redirected in a Home Assistant event: `kodi_call_method_result`.'
|
|
||||||
fields:
|
|
||||||
entity_id:
|
|
||||||
description: Name(s) of the Kodi entities where to run the API method.
|
|
||||||
example: 'media_player.living_room_kodi'
|
|
||||||
method:
|
|
||||||
description: Name of the Kodi JSONRPC API method to be called.
|
|
||||||
example: 'VideoLibrary.GetRecentlyAddedEpisodes'
|
|
||||||
|
|
||||||
squeezebox_call_method:
|
squeezebox_call_method:
|
||||||
description: 'Call a Squeezebox JSON/RPC API method.'
|
description: 'Call a Squeezebox JSON/RPC API method.'
|
||||||
fields:
|
fields:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user