Use push updates in Apple TV (#6323)

* Use push updates in Apple TV

* Fix review comments
This commit is contained in:
Pierre Ståhl 2017-03-02 09:12:55 +01:00 committed by Paulus Schoutsen
parent a08539d88d
commit bae6333c26
2 changed files with 58 additions and 28 deletions

View file

@ -8,7 +8,6 @@ import asyncio
import logging
import hashlib
import aiohttp
import voluptuous as vol
from homeassistant.core import callback
@ -19,13 +18,13 @@ from homeassistant.components.media_player import (
MEDIA_TYPE_VIDEO, MEDIA_TYPE_TVSHOW)
from homeassistant.const import (
STATE_IDLE, STATE_PAUSED, STATE_PLAYING, STATE_STANDBY, CONF_HOST,
STATE_OFF, CONF_NAME)
STATE_OFF, CONF_NAME, EVENT_HOMEASSISTANT_STOP)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
import homeassistant.util.dt as dt_util
REQUIREMENTS = ['pyatv==0.1.4']
REQUIREMENTS = ['pyatv==0.2.1']
_LOGGER = logging.getLogger(__name__)
@ -73,7 +72,14 @@ def async_setup_platform(hass, config, async_add_entities,
atv = pyatv.connect_to_apple_tv(details, hass.loop, session=session)
entity = AppleTvDevice(atv, name, start_off)
yield from async_add_entities([entity], update_before_add=True)
@callback
def on_hass_stop(event):
"""Stop push updates when hass stops."""
atv.push_updater.stop()
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, on_hass_stop)
yield from async_add_entities([entity])
class AppleTvDevice(MediaPlayerDevice):
@ -86,18 +92,34 @@ class AppleTvDevice(MediaPlayerDevice):
self._is_off = is_off
self._playing = None
self._artwork_hash = None
self._atv.push_updater.listener = self
@asyncio.coroutine
def async_added_to_hass(self):
"""Called when entity is about to be added to HASS."""
self._atv.push_updater.start()
@callback
def _set_power_off(self, is_off):
self._playing = None
self._artwork_hash = None
self._is_off = is_off
if is_off:
self._atv.push_updater.stop()
else:
self._atv.push_updater.start()
self.hass.async_add_job(self.async_update_ha_state())
@property
def name(self):
"""Return the name of the device."""
return self._name
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def state(self):
"""Return the state of the device."""
@ -120,29 +142,19 @@ class AppleTvDevice(MediaPlayerDevice):
else:
return STATE_STANDBY # Bad or unknown state?
@asyncio.coroutine
def async_update(self):
"""Retrieve latest state."""
if self._is_off:
return
@callback
def playstatus_update(self, updater, playing):
"""Print what is currently playing when it changes."""
if self.state == STATE_IDLE:
self._artwork_hash = None
elif self._has_playing_media_changed(playing):
base = str(playing.title) + str(playing.artist) + \
str(playing.album) + str(playing.total_time)
self._artwork_hash = hashlib.md5(
base.encode('utf-8')).hexdigest()
from pyatv import exceptions
try:
playing = yield from self._atv.metadata.playing()
if self._has_playing_media_changed(playing):
base = str(playing.title) + str(playing.artist) + \
str(playing.album) + str(playing.total_time)
self._artwork_hash = hashlib.md5(
base.encode('utf-8')).hexdigest()
self._playing = playing
except exceptions.AuthenticationError as ex:
_LOGGER.warning('%s (bad login id?)', str(ex))
except aiohttp.errors.ClientOSError as ex:
_LOGGER.error('failed to connect to Apple TV (%s)', str(ex))
except asyncio.TimeoutError:
_LOGGER.warning('timed out while connecting to Apple TV')
self._playing = playing
self.hass.async_add_job(self.async_update_ha_state())
def _has_playing_media_changed(self, new_playing):
if self._playing is None:
@ -151,6 +163,21 @@ class AppleTvDevice(MediaPlayerDevice):
return new_playing.media_type != old_playing.media_type or \
new_playing.title != old_playing.title
@callback
def playstatus_error(self, updater, exception):
"""Inform about an error and restart push updates."""
_LOGGER.warning('A %s error occurred: %s',
exception.__class__, exception)
# This will wait 10 seconds before restarting push updates. If the
# connection continues to fail, it will flood the log (every 10
# seconds) until it succeeds. A better approach should probably be
# implemented here later.
updater.start(initial_delay=10)
self._playing = None
self._artwork_hash = None
self.hass.async_add_job(self.async_update_ha_state())
@property
def media_content_type(self):
"""Content type of current playing media."""
@ -191,7 +218,8 @@ class AppleTvDevice(MediaPlayerDevice):
@property
def media_image_hash(self):
"""Hash value for media image."""
return self._artwork_hash
if self.state != STATE_IDLE:
return self._artwork_hash
@asyncio.coroutine
def async_get_media_image(self):
@ -207,6 +235,8 @@ class AppleTvDevice(MediaPlayerDevice):
title = self._playing.title
return title if title else "No title"
return 'Not connected to Apple TV'
@property
def supported_features(self):
"""Flag media player features that are supported."""

View file

@ -454,7 +454,7 @@ pyasn1-modules==0.0.8
pyasn1==0.2.2
# homeassistant.components.media_player.apple_tv
pyatv==0.1.4
pyatv==0.2.1
# homeassistant.components.device_tracker.bbox
# homeassistant.components.sensor.bbox