Mediaroom async (#13321)

* Initial commit on new asyncio based version

* Async version

* Lint

* updated to lasted pymediaroom version

* bump version

* optimistic state updates

* bump version

* Moved class import to method import

* async schedule and name correction

* Addresses @balloob comments

* missed fixes

* no unique_id for configuration based STB

* hound

* handle 2 mediaroom platforms
This commit is contained in:
Diogo Gomes 2018-03-22 01:06:41 +00:00 committed by Paulus Schoutsen
parent 17cbd0f3c9
commit 1676df6a5f
2 changed files with 230 additions and 112 deletions

View file

@ -9,134 +9,182 @@ import logging
import voluptuous as vol
from homeassistant.components.media_player import (
MEDIA_TYPE_CHANNEL, SUPPORT_PAUSE, SUPPORT_PLAY_MEDIA,
SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_STOP, PLATFORM_SCHEMA,
SUPPORT_NEXT_TRACK, SUPPORT_PREVIOUS_TRACK, SUPPORT_PLAY,
SUPPORT_VOLUME_STEP, SUPPORT_VOLUME_MUTE,
MediaPlayerDevice)
MEDIA_TYPE_CHANNEL, SUPPORT_PAUSE, SUPPORT_PLAY_MEDIA, SUPPORT_TURN_OFF,
SUPPORT_TURN_ON, SUPPORT_STOP, PLATFORM_SCHEMA, SUPPORT_NEXT_TRACK,
SUPPORT_PREVIOUS_TRACK, SUPPORT_PLAY, SUPPORT_VOLUME_STEP,
SUPPORT_VOLUME_MUTE, MediaPlayerDevice,
)
from homeassistant.helpers.dispatcher import (
dispatcher_send, async_dispatcher_connect
)
from homeassistant.const import (
CONF_HOST, CONF_NAME, CONF_OPTIMISTIC, CONF_TIMEOUT,
STATE_PAUSED, STATE_PLAYING, STATE_STANDBY,
STATE_ON)
CONF_HOST, CONF_NAME, CONF_OPTIMISTIC, STATE_OFF,
CONF_TIMEOUT, STATE_PAUSED, STATE_PLAYING, STATE_STANDBY,
STATE_UNAVAILABLE
)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['pymediaroom==0.5']
REQUIREMENTS = ['pymediaroom==0.6']
_LOGGER = logging.getLogger(__name__)
NOTIFICATION_TITLE = 'Mediaroom Media Player Setup'
NOTIFICATION_ID = 'mediaroom_notification'
DEFAULT_NAME = 'Mediaroom STB'
DEFAULT_TIMEOUT = 9
DATA_MEDIAROOM = "mediaroom_known_stb"
DISCOVERY_MEDIAROOM = "mediaroom_discovery_installed"
SIGNAL_STB_NOTIFY = 'mediaroom_stb_discovered'
SUPPORT_MEDIAROOM = SUPPORT_PAUSE | SUPPORT_TURN_ON | SUPPORT_TURN_OFF \
| SUPPORT_VOLUME_STEP | SUPPORT_VOLUME_MUTE | SUPPORT_PLAY_MEDIA \
| SUPPORT_STOP | SUPPORT_NEXT_TRACK | SUPPORT_PREVIOUS_TRACK \
| SUPPORT_PLAY
SUPPORT_MEDIAROOM = SUPPORT_PAUSE | SUPPORT_TURN_ON | SUPPORT_TURN_OFF | \
SUPPORT_VOLUME_STEP | SUPPORT_VOLUME_MUTE | \
SUPPORT_PLAY_MEDIA | SUPPORT_STOP | SUPPORT_NEXT_TRACK | \
SUPPORT_PREVIOUS_TRACK | SUPPORT_PLAY
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_HOST): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_OPTIMISTIC, default=False): cv.boolean,
vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
})
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_HOST): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_OPTIMISTIC, default=False): cv.boolean,
vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
}
)
def setup_platform(hass, config, add_devices, discovery_info=None):
async def async_setup_platform(hass, config, async_add_devices,
discovery_info=None):
"""Set up the Mediaroom platform."""
hosts = []
known_hosts = hass.data.get(DATA_MEDIAROOM)
if known_hosts is None:
known_hosts = hass.data[DATA_MEDIAROOM] = []
host = config.get(CONF_HOST, None)
if host is None:
_LOGGER.info("Trying to discover Mediaroom STB")
if host:
async_add_devices([MediaroomDevice(host=host,
device_id=None,
optimistic=config[CONF_OPTIMISTIC],
timeout=config[CONF_TIMEOUT])])
hass.data[DATA_MEDIAROOM].append(host)
from pymediaroom import Remote
_LOGGER.debug("Trying to discover Mediaroom STB")
host = Remote.discover(known_hosts)
if host is None:
_LOGGER.warning("Can't find any STB")
def callback_notify(notify):
"""Process NOTIFY message from STB."""
if notify.ip_address in hass.data[DATA_MEDIAROOM]:
dispatcher_send(hass, SIGNAL_STB_NOTIFY, notify)
return
hosts.append(host)
known_hosts.append(host)
stbs = []
_LOGGER.debug("Discovered new stb %s", notify.ip_address)
hass.data[DATA_MEDIAROOM].append(notify.ip_address)
new_stb = MediaroomDevice(
host=notify.ip_address, device_id=notify.device_uuid,
optimistic=False
)
async_add_devices([new_stb])
try:
for host in hosts:
stbs.append(MediaroomDevice(
config.get(CONF_NAME),
host,
config.get(CONF_OPTIMISTIC),
config.get(CONF_TIMEOUT)
))
if not config[CONF_OPTIMISTIC]:
from pymediaroom import install_mediaroom_protocol
except ConnectionRefusedError:
hass.components.persistent_notification.create(
'Error: Unable to initialize mediaroom at {}<br />'
'Check its network connection or consider '
'using auto discovery.<br />'
'You will need to restart hass after fixing.'
''.format(host),
title=NOTIFICATION_TITLE,
notification_id=NOTIFICATION_ID)
add_devices(stbs)
already_installed = hass.data.get(DISCOVERY_MEDIAROOM, False)
if not already_installed:
await install_mediaroom_protocol(
responses_callback=callback_notify)
_LOGGER.debug("Auto discovery installed")
hass.data[DISCOVERY_MEDIAROOM] = True
class MediaroomDevice(MediaPlayerDevice):
"""Representation of a Mediaroom set-up-box on the network."""
def __init__(self, name, host, optimistic=False, timeout=DEFAULT_TIMEOUT):
def set_state(self, mediaroom_state):
"""Helper method to map pymediaroom states to HA states."""
from pymediaroom import State
state_map = {
State.OFF: STATE_OFF,
State.STANDBY: STATE_STANDBY,
State.PLAYING_LIVE_TV: STATE_PLAYING,
State.PLAYING_RECORDED_TV: STATE_PLAYING,
State.PLAYING_TIMESHIFT_TV: STATE_PLAYING,
State.STOPPED: STATE_PAUSED,
State.UNKNOWN: STATE_UNAVAILABLE
}
self._state = state_map[mediaroom_state]
def __init__(self, host, device_id, optimistic=False,
timeout=DEFAULT_TIMEOUT):
"""Initialize the device."""
from pymediaroom import Remote
self.stb = Remote(host, timeout=timeout)
_LOGGER.info(
"Found %s at %s%s", name, host,
" - I'm optimistic" if optimistic else "")
self._name = name
self._is_standby = not optimistic
self._current = None
self.host = host
self.stb = Remote(host)
_LOGGER.info("Found STB at %s%s", host,
" - I'm optimistic" if optimistic else "")
self._channel = None
self._optimistic = optimistic
self._state = STATE_STANDBY
self._state = STATE_PLAYING if optimistic else STATE_STANDBY
self._name = 'Mediaroom {}'.format(device_id)
self._available = True
if device_id:
self._unique_id = device_id
else:
self._unique_id = None
def update(self):
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def available(self):
"""Return True if entity is available."""
return self._available
async def async_added_to_hass(self):
"""Retrieve latest state."""
if not self._optimistic:
self._is_standby = self.stb.get_standby()
if self._is_standby:
self._state = STATE_STANDBY
elif self._state not in [STATE_PLAYING, STATE_PAUSED]:
self._state = STATE_PLAYING
_LOGGER.debug(
"%s(%s) is [%s]",
self._name, self.stb.stb_ip, self._state)
async def async_notify_received(notify):
"""Process STB state from NOTIFY message."""
stb_state = self.stb.notify_callback(notify)
# stb_state is None in case the notify is not from the current stb
if not stb_state:
return
self.set_state(stb_state)
_LOGGER.debug("STB(%s) is [%s]", self.host, self._state)
self._available = True
self.async_schedule_update_ha_state()
def play_media(self, media_type, media_id, **kwargs):
async_dispatcher_connect(self.hass, SIGNAL_STB_NOTIFY,
async_notify_received)
async def async_play_media(self, media_type, media_id, **kwargs):
"""Play media."""
_LOGGER.debug(
"%s(%s) Play media: %s (%s)",
self._name, self.stb.stb_ip, media_id, media_type)
from pymediaroom import PyMediaroomError
_LOGGER.debug("STB(%s) Play media: %s (%s)", self.stb.stb_ip,
media_id, media_type)
if media_type != MEDIA_TYPE_CHANNEL:
_LOGGER.error('invalid media type')
return
if media_id.isdigit():
media_id = int(media_id)
else:
if not media_id.isdigit():
_LOGGER.error("media_id must be a channel number")
return
self.stb.send_cmd(media_id)
self._state = STATE_PLAYING
try:
await self.stb.send_cmd(int(media_id))
if self._optimistic:
self._state = STATE_PLAYING
self._available = True
except PyMediaroomError:
self._available = False
self.async_schedule_update_ha_state()
@property
def unique_id(self):
"""Return a unique ID."""
return self._unique_id
@property
def name(self):
"""Return the name of the device."""
return self._name
# MediaPlayerDevice properties and methods
@property
def state(self):
"""Return the state of the device."""
@ -152,50 +200,120 @@ class MediaroomDevice(MediaPlayerDevice):
"""Return the content type of current playing media."""
return MEDIA_TYPE_CHANNEL
def turn_on(self):
@property
def media_channel(self):
"""Channel currently playing."""
return self._channel
async def async_turn_on(self):
"""Turn on the receiver."""
self.stb.send_cmd('Power')
self._state = STATE_ON
from pymediaroom import PyMediaroomError
try:
self.set_state(await self.stb.turn_on())
if self._optimistic:
self._state = STATE_PLAYING
self._available = True
except PyMediaroomError:
self._available = False
self.async_schedule_update_ha_state()
def turn_off(self):
async def async_turn_off(self):
"""Turn off the receiver."""
self.stb.send_cmd('Power')
self._state = STATE_STANDBY
from pymediaroom import PyMediaroomError
try:
self.set_state(await self.stb.turn_off())
if self._optimistic:
self._state = STATE_STANDBY
self._available = True
except PyMediaroomError:
self._available = False
self.async_schedule_update_ha_state()
def media_play(self):
async def async_media_play(self):
"""Send play command."""
_LOGGER.debug("media_play()")
self.stb.send_cmd('PlayPause')
self._state = STATE_PLAYING
from pymediaroom import PyMediaroomError
try:
_LOGGER.debug("media_play()")
await self.stb.send_cmd('PlayPause')
if self._optimistic:
self._state = STATE_PLAYING
self._available = True
except PyMediaroomError:
self._available = False
self.async_schedule_update_ha_state()
def media_pause(self):
async def async_media_pause(self):
"""Send pause command."""
self.stb.send_cmd('PlayPause')
self._state = STATE_PAUSED
from pymediaroom import PyMediaroomError
try:
await self.stb.send_cmd('PlayPause')
if self._optimistic:
self._state = STATE_PAUSED
self._available = True
except PyMediaroomError:
self._available = False
self.async_schedule_update_ha_state()
def media_stop(self):
async def async_media_stop(self):
"""Send stop command."""
self.stb.send_cmd('Stop')
self._state = STATE_PAUSED
from pymediaroom import PyMediaroomError
try:
await self.stb.send_cmd('Stop')
if self._optimistic:
self._state = STATE_PAUSED
self._available = True
except PyMediaroomError:
self._available = False
self.async_schedule_update_ha_state()
def media_previous_track(self):
async def async_media_previous_track(self):
"""Send Program Down command."""
self.stb.send_cmd('ProgDown')
self._state = STATE_PLAYING
from pymediaroom import PyMediaroomError
try:
await self.stb.send_cmd('ProgDown')
if self._optimistic:
self._state = STATE_PLAYING
self._available = True
except PyMediaroomError:
self._available = False
self.async_schedule_update_ha_state()
def media_next_track(self):
async def async_media_next_track(self):
"""Send Program Up command."""
self.stb.send_cmd('ProgUp')
self._state = STATE_PLAYING
from pymediaroom import PyMediaroomError
try:
await self.stb.send_cmd('ProgUp')
if self._optimistic:
self._state = STATE_PLAYING
self._available = True
except PyMediaroomError:
self._available = False
self.async_schedule_update_ha_state()
def volume_up(self):
async def async_volume_up(self):
"""Send volume up command."""
self.stb.send_cmd('VolUp')
from pymediaroom import PyMediaroomError
try:
await self.stb.send_cmd('VolUp')
self._available = True
except PyMediaroomError:
self._available = False
self.async_schedule_update_ha_state()
def volume_down(self):
async def async_volume_down(self):
"""Send volume up command."""
self.stb.send_cmd('VolDown')
from pymediaroom import PyMediaroomError
try:
await self.stb.send_cmd('VolDown')
except PyMediaroomError:
self._available = False
self.async_schedule_update_ha_state()
def mute_volume(self, mute):
async def async_mute_volume(self, mute):
"""Send mute command."""
self.stb.send_cmd('Mute')
from pymediaroom import PyMediaroomError
try:
await self.stb.send_cmd('Mute')
except PyMediaroomError:
self._available = False
self.async_schedule_update_ha_state()

View file

@ -804,7 +804,7 @@ pylutron==0.1.0
pymailgunner==1.4
# homeassistant.components.media_player.mediaroom
pymediaroom==0.5
pymediaroom==0.6
# homeassistant.components.media_player.xiaomi_tv
pymitv==1.0.0