diff --git a/homeassistant/components/media_player/kodi.py b/homeassistant/components/media_player/kodi.py index 18c01c396ac..9861887df89 100644 --- a/homeassistant/components/media_player/kodi.py +++ b/homeassistant/components/media_player/kodi.py @@ -24,7 +24,7 @@ from homeassistant.components.media_player import ( from homeassistant.const import ( STATE_IDLE, STATE_OFF, STATE_PAUSED, STATE_PLAYING, CONF_HOST, CONF_NAME, CONF_PORT, CONF_SSL, CONF_PROXY_SSL, CONF_USERNAME, CONF_PASSWORD, - EVENT_HOMEASSISTANT_STOP) + CONF_TIMEOUT, EVENT_HOMEASSISTANT_STOP) from homeassistant.core import callback from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv @@ -34,6 +34,8 @@ REQUIREMENTS = ['jsonrpc-async==0.6', 'jsonrpc-websocket==0.5'] _LOGGER = logging.getLogger(__name__) +EVENT_KODI_CALL_METHOD_RESULT = 'kodi_call_method_result' + CONF_TCP_PORT = 'tcp_port' CONF_TURN_OFF_ACTION = 'turn_off_action' CONF_ENABLE_WEBSOCKET = 'enable_websocket' @@ -74,6 +76,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_TCP_PORT, default=DEFAULT_TCP_PORT): cv.port, vol.Optional(CONF_PROXY_SSL, default=DEFAULT_PROXY_SSL): cv.boolean, vol.Optional(CONF_TURN_OFF_ACTION, default=None): vol.In(TURN_OFF_ACTION), + vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int, vol.Inclusive(CONF_USERNAME, 'auth'): cv.string, vol.Inclusive(CONF_PASSWORD, 'auth'): cv.string, vol.Optional(CONF_ENABLE_WEBSOCKET, default=DEFAULT_ENABLE_WEBSOCKET): @@ -81,6 +84,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ }) SERVICE_ADD_MEDIA = 'kodi_add_to_playlist' +SERVICE_CALL_METHOD = 'kodi_call_method' DATA_KODI = 'kodi' @@ -88,6 +92,7 @@ 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_ADD_MEDIA_SCHEMA = MEDIA_PLAYER_SCHEMA.extend({ vol.Required(ATTR_MEDIA_TYPE): cv.string, @@ -95,11 +100,17 @@ MEDIA_PLAYER_ADD_MEDIA_SCHEMA = MEDIA_PLAYER_SCHEMA.extend({ 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}, } @@ -127,7 +138,8 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): host=host, port=port, tcp_port=tcp_port, encryption=encryption, username=config.get(CONF_USERNAME), password=config.get(CONF_PASSWORD), - turn_off_action=config.get(CONF_TURN_OFF_ACTION), websocket=websocket) + turn_off_action=config.get(CONF_TURN_OFF_ACTION), + timeout=config.get(CONF_TIMEOUT), websocket=websocket) hass.data[DATA_KODI].append(entity) async_add_devices([entity], update_before_add=True) @@ -199,7 +211,7 @@ class KodiDevice(MediaPlayerDevice): def __init__(self, hass, name, host, port, tcp_port, encryption=False, username=None, password=None, turn_off_action=None, - websocket=True): + timeout=DEFAULT_TIMEOUT, websocket=True): """Initialize the Kodi device.""" import jsonrpc_async import jsonrpc_websocket @@ -207,7 +219,7 @@ class KodiDevice(MediaPlayerDevice): self._name = name kwargs = { - 'timeout': DEFAULT_TIMEOUT, + 'timeout': timeout, 'session': async_get_clientsession(hass), } @@ -678,6 +690,30 @@ class KodiDevice(MediaPlayerDevice): yield from self.server.Player.SetShuffle( {"playerid": self._players[0]['playerid'], "shuffle": shuffle}) + @asyncio.coroutine + def async_call_method(self, method, **kwargs): + """Run Kodi JSONRPC API method with params.""" + import jsonrpc_base + _LOGGER.debug('Run API method "%s", kwargs=%s', method, kwargs) + result_ok = False + try: + result = yield from getattr(self.server, method)(**kwargs) + result_ok = True + except jsonrpc_base.jsonrpc.ProtocolError as exc: + result = exc.args[2]['error'] + _LOGGER.error('Run API method %s.%s(%s) error: %s', + self.entity_id, method, kwargs, result) + + if isinstance(result, dict): + event_data = {'entity_id': self.entity_id, + 'result': result, + 'result_ok': result_ok, + 'input': {'method': method, 'params': kwargs}} + _LOGGER.debug('EVENT kodi_call_method_result: %s', event_data) + self.hass.bus.async_fire(EVENT_KODI_CALL_METHOD_RESULT, + event_data=event_data) + return result + @asyncio.coroutine def async_add_media_to_playlist( self, media_type, media_id=None, media_name='ALL', artist_name=''): diff --git a/homeassistant/components/media_player/services.yaml b/homeassistant/components/media_player/services.yaml index 4d5f85c05eb..00ce0987fd9 100644 --- a/homeassistant/components/media_player/services.yaml +++ b/homeassistant/components/media_player/services.yaml @@ -289,3 +289,14 @@ kodi_add_to_playlist: 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'