Convert Kodi platform to async (#5167)
* Convert Kodi platform to async * Remove unnecessary async_update_ha_state
This commit is contained in:
parent
6b682d0d81
commit
276a29c8f4
2 changed files with 127 additions and 85 deletions
|
@ -4,9 +4,11 @@ Support for interfacing with the XBMC/Kodi JSON-RPC API.
|
|||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/media_player.kodi/
|
||||
"""
|
||||
import asyncio
|
||||
import logging
|
||||
import urllib
|
||||
|
||||
import aiohttp
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.media_player import (
|
||||
|
@ -16,9 +18,10 @@ 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_USERNAME, CONF_PASSWORD)
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
REQUIREMENTS = ['jsonrpc-requests==0.3']
|
||||
REQUIREMENTS = ['jsonrpc-async==0.1']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -26,6 +29,7 @@ CONF_TURN_OFF_ACTION = 'turn_off_action'
|
|||
|
||||
DEFAULT_NAME = 'Kodi'
|
||||
DEFAULT_PORT = 8080
|
||||
DEFAULT_TIMEOUT = 5
|
||||
|
||||
TURN_OFF_ACTION = [None, 'quit', 'hibernate', 'suspend', 'reboot', 'shutdown']
|
||||
|
||||
|
@ -43,7 +47,9 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
|||
})
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
@asyncio.coroutine
|
||||
def async_setup_platform(hass, config, async_add_entities,
|
||||
discovery_info=None):
|
||||
"""Setup the Kodi platform."""
|
||||
host = config.get(CONF_HOST)
|
||||
port = config.get(CONF_PORT)
|
||||
|
@ -55,29 +61,34 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
|||
"definitions here: "
|
||||
"https://home-assistant.io/components/media_player.kodi/")
|
||||
|
||||
add_entities([
|
||||
KodiDevice(
|
||||
config.get(CONF_NAME),
|
||||
host=host, port=port,
|
||||
username=config.get(CONF_USERNAME),
|
||||
password=config.get(CONF_PASSWORD),
|
||||
turn_off_action=config.get(CONF_TURN_OFF_ACTION)),
|
||||
])
|
||||
entity = KodiDevice(
|
||||
hass,
|
||||
name=config.get(CONF_NAME),
|
||||
host=host, port=port,
|
||||
username=config.get(CONF_USERNAME),
|
||||
password=config.get(CONF_PASSWORD),
|
||||
turn_off_action=config.get(CONF_TURN_OFF_ACTION))
|
||||
|
||||
yield from async_add_entities([entity], update_before_add=True)
|
||||
|
||||
|
||||
class KodiDevice(MediaPlayerDevice):
|
||||
"""Representation of a XBMC/Kodi device."""
|
||||
|
||||
def __init__(self, name, host, port, username=None, password=None,
|
||||
def __init__(self, hass, name, host, port, username=None, password=None,
|
||||
turn_off_action=None):
|
||||
"""Initialize the Kodi device."""
|
||||
import jsonrpc_requests
|
||||
import jsonrpc_async
|
||||
self.hass = hass
|
||||
self._name = name
|
||||
|
||||
kwargs = {'timeout': 5}
|
||||
kwargs = {
|
||||
'timeout': DEFAULT_TIMEOUT,
|
||||
'session': async_get_clientsession(hass),
|
||||
}
|
||||
|
||||
if username is not None:
|
||||
kwargs['auth'] = (username, password)
|
||||
kwargs['auth'] = aiohttp.BasicAuth(username, password)
|
||||
image_auth_string = "{}:{}@".format(username, password)
|
||||
else:
|
||||
image_auth_string = ""
|
||||
|
@ -86,26 +97,26 @@ class KodiDevice(MediaPlayerDevice):
|
|||
self._image_url = 'http://{}{}:{}/image'.format(
|
||||
image_auth_string, host, port)
|
||||
|
||||
self._server = jsonrpc_requests.Server(self._http_url, **kwargs)
|
||||
self._server = jsonrpc_async.Server(self._http_url, **kwargs)
|
||||
|
||||
self._turn_off_action = turn_off_action
|
||||
self._players = list()
|
||||
self._properties = None
|
||||
self._item = None
|
||||
self._app_properties = None
|
||||
self.update()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the device."""
|
||||
return self._name
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_players(self):
|
||||
"""Return the active player objects or None."""
|
||||
import jsonrpc_requests
|
||||
import jsonrpc_async
|
||||
try:
|
||||
return self._server.Player.GetActivePlayers()
|
||||
except jsonrpc_requests.jsonrpc.TransportError:
|
||||
return (yield from self._server.Player.GetActivePlayers())
|
||||
except jsonrpc_async.jsonrpc.TransportError:
|
||||
if self._players is not None:
|
||||
_LOGGER.info('Unable to fetch kodi data')
|
||||
_LOGGER.debug('Unable to fetch kodi data', exc_info=True)
|
||||
|
@ -125,28 +136,30 @@ class KodiDevice(MediaPlayerDevice):
|
|||
else:
|
||||
return STATE_PLAYING
|
||||
|
||||
def update(self):
|
||||
@asyncio.coroutine
|
||||
def async_update(self):
|
||||
"""Retrieve latest state."""
|
||||
self._players = self._get_players()
|
||||
self._players = yield from self._get_players()
|
||||
|
||||
if self._players is not None and len(self._players) > 0:
|
||||
player_id = self._players[0]['playerid']
|
||||
|
||||
assert isinstance(player_id, int)
|
||||
|
||||
self._properties = self._server.Player.GetProperties(
|
||||
self._properties = yield from self._server.Player.GetProperties(
|
||||
player_id,
|
||||
['time', 'totaltime', 'speed', 'live']
|
||||
)
|
||||
|
||||
self._item = self._server.Player.GetItem(
|
||||
self._item = (yield from self._server.Player.GetItem(
|
||||
player_id,
|
||||
['title', 'file', 'uniqueid', 'thumbnail', 'artist']
|
||||
)['item']
|
||||
))['item']
|
||||
|
||||
self._app_properties = self._server.Application.GetProperties(
|
||||
['volume', 'muted']
|
||||
)
|
||||
self._app_properties = \
|
||||
yield from self._server.Application.GetProperties(
|
||||
['volume', 'muted']
|
||||
)
|
||||
else:
|
||||
self._properties = None
|
||||
self._item = None
|
||||
|
@ -218,94 +231,118 @@ class KodiDevice(MediaPlayerDevice):
|
|||
|
||||
return supported_media_commands
|
||||
|
||||
def turn_off(self):
|
||||
@asyncio.coroutine
|
||||
def async_turn_off(self):
|
||||
"""Execute turn_off_action to turn off media player."""
|
||||
if self._turn_off_action == 'quit':
|
||||
self._server.Application.Quit()
|
||||
yield from self._server.Application.Quit()
|
||||
elif self._turn_off_action == 'hibernate':
|
||||
self._server.System.Hibernate()
|
||||
yield from self._server.System.Hibernate()
|
||||
elif self._turn_off_action == 'suspend':
|
||||
self._server.System.Suspend()
|
||||
yield from self._server.System.Suspend()
|
||||
elif self._turn_off_action == 'reboot':
|
||||
self._server.System.Reboot()
|
||||
yield from self._server.System.Reboot()
|
||||
elif self._turn_off_action == 'shutdown':
|
||||
self._server.System.Shutdown()
|
||||
yield from self._server.System.Shutdown()
|
||||
else:
|
||||
_LOGGER.warning('turn_off requested but turn_off_action is none')
|
||||
|
||||
self.update_ha_state()
|
||||
|
||||
def volume_up(self):
|
||||
@asyncio.coroutine
|
||||
def async_volume_up(self):
|
||||
"""Volume up the media player."""
|
||||
assert self._server.Input.ExecuteAction('volumeup') == 'OK'
|
||||
self.update_ha_state()
|
||||
assert (
|
||||
yield from self._server.Input.ExecuteAction('volumeup')) == 'OK'
|
||||
|
||||
def volume_down(self):
|
||||
@asyncio.coroutine
|
||||
def async_volume_down(self):
|
||||
"""Volume down the media player."""
|
||||
assert self._server.Input.ExecuteAction('volumedown') == 'OK'
|
||||
self.update_ha_state()
|
||||
assert (
|
||||
yield from self._server.Input.ExecuteAction('volumedown')) == 'OK'
|
||||
|
||||
def set_volume_level(self, volume):
|
||||
"""Set volume level, range 0..1."""
|
||||
self._server.Application.SetVolume(int(volume * 100))
|
||||
self.update_ha_state()
|
||||
def async_set_volume_level(self, volume):
|
||||
"""Set volume level, range 0..1.
|
||||
|
||||
def mute_volume(self, mute):
|
||||
"""Mute (true) or unmute (false) media player."""
|
||||
self._server.Application.SetMute(mute)
|
||||
self.update_ha_state()
|
||||
This method must be run in the event loop and returns a coroutine.
|
||||
"""
|
||||
return self._server.Application.SetVolume(int(volume * 100))
|
||||
|
||||
def _set_play_state(self, state):
|
||||
def async_mute_volume(self, mute):
|
||||
"""Mute (true) or unmute (false) media player.
|
||||
|
||||
This method must be run in the event loop and returns a coroutine.
|
||||
"""
|
||||
return self._server.Application.SetMute(mute)
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_set_play_state(self, state):
|
||||
"""Helper method for play/pause/toggle."""
|
||||
players = self._get_players()
|
||||
players = yield from self._get_players()
|
||||
|
||||
if len(players) != 0:
|
||||
self._server.Player.PlayPause(players[0]['playerid'], state)
|
||||
yield from self._server.Player.PlayPause(
|
||||
players[0]['playerid'], state)
|
||||
|
||||
self.update_ha_state()
|
||||
def async_media_play_pause(self):
|
||||
"""Pause media on media player.
|
||||
|
||||
def media_play_pause(self):
|
||||
"""Pause media on media player."""
|
||||
self._set_play_state('toggle')
|
||||
This method must be run in the event loop and returns a coroutine.
|
||||
"""
|
||||
return self.async_set_play_state('toggle')
|
||||
|
||||
def media_play(self):
|
||||
"""Play media."""
|
||||
self._set_play_state(True)
|
||||
def async_media_play(self):
|
||||
"""Play media.
|
||||
|
||||
def media_pause(self):
|
||||
"""Pause the media player."""
|
||||
self._set_play_state(False)
|
||||
This method must be run in the event loop and returns a coroutine.
|
||||
"""
|
||||
return self.async_set_play_state(True)
|
||||
|
||||
def media_stop(self):
|
||||
def async_media_pause(self):
|
||||
"""Pause the media player.
|
||||
|
||||
This method must be run in the event loop and returns a coroutine.
|
||||
"""
|
||||
return self.async_set_play_state(False)
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_media_stop(self):
|
||||
"""Stop the media player."""
|
||||
players = self._get_players()
|
||||
players = yield from self._get_players()
|
||||
|
||||
if len(players) != 0:
|
||||
self._server.Player.Stop(players[0]['playerid'])
|
||||
yield from self._server.Player.Stop(players[0]['playerid'])
|
||||
|
||||
@asyncio.coroutine
|
||||
def _goto(self, direction):
|
||||
"""Helper method used for previous/next track."""
|
||||
players = self._get_players()
|
||||
players = yield from self._get_players()
|
||||
|
||||
if len(players) != 0:
|
||||
self._server.Player.GoTo(players[0]['playerid'], direction)
|
||||
if direction == 'previous':
|
||||
# first seek to position 0. Kodi goes to the beginning of the
|
||||
# current track if the current track is not at the beginning.
|
||||
yield from self._server.Player.Seek(players[0]['playerid'], 0)
|
||||
|
||||
self.update_ha_state()
|
||||
yield from self._server.Player.GoTo(
|
||||
players[0]['playerid'], direction)
|
||||
|
||||
def media_next_track(self):
|
||||
"""Send next track command."""
|
||||
self._goto('next')
|
||||
def async_media_next_track(self):
|
||||
"""Send next track command.
|
||||
|
||||
def media_previous_track(self):
|
||||
"""Send next track command."""
|
||||
# first seek to position 0, Kodi seems to go to the beginning
|
||||
# of the current track current track is not at the beginning
|
||||
self.media_seek(0)
|
||||
self._goto('previous')
|
||||
This method must be run in the event loop and returns a coroutine.
|
||||
"""
|
||||
return self._goto('next')
|
||||
|
||||
def media_seek(self, position):
|
||||
def async_media_previous_track(self):
|
||||
"""Send next track command.
|
||||
|
||||
This method must be run in the event loop and returns a coroutine.
|
||||
"""
|
||||
return self._goto('previous')
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_media_seek(self, position):
|
||||
"""Send seek command."""
|
||||
players = self._get_players()
|
||||
players = yield from self._get_players()
|
||||
|
||||
time = {}
|
||||
|
||||
|
@ -321,13 +358,16 @@ class KodiDevice(MediaPlayerDevice):
|
|||
time['hours'] = int(position)
|
||||
|
||||
if len(players) != 0:
|
||||
self._server.Player.Seek(players[0]['playerid'], time)
|
||||
yield from self._server.Player.Seek(players[0]['playerid'], time)
|
||||
|
||||
self.update_ha_state()
|
||||
def async_play_media(self, media_type, media_id, **kwargs):
|
||||
"""Send the play_media command to the media player.
|
||||
|
||||
def play_media(self, media_type, media_id, **kwargs):
|
||||
"""Send the play_media command to the media player."""
|
||||
This method must be run in the event loop and returns a coroutine.
|
||||
"""
|
||||
if media_type == "CHANNEL":
|
||||
self._server.Player.Open({"item": {"channelid": int(media_id)}})
|
||||
return self._server.Player.Open(
|
||||
{"item": {"channelid": int(media_id)}})
|
||||
else:
|
||||
self._server.Player.Open({"item": {"file": str(media_id)}})
|
||||
return self._server.Player.Open(
|
||||
{"item": {"file": str(media_id)}})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue