Add HEOS media player component (#21721)

## Description:
Denon HEOS media player.

**Pull request in [home-assistant.io](https://github.com/home-assistant/home-assistant.io) with documentation (if applicable):** home-assistant/home-assistant.io#8848

## Example entry for `configuration.yaml` (if applicable):
```yaml
heos:
  host: HEOS-1                              
```

## Checklist:
  - [X] The code change is tested and works locally.
  - [X] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass**
  - [X] There is no commented out code in this PR.

If user exposed functionality or configuration variables are added/changed:
  - [X] Documentation added/updated in [home-assistant.io](https://github.com/home-assistant/home-assistant.io)

If the code communicates with devices, web services, or third-party tools:
  - [X] New dependencies have been added to the `REQUIREMENTS` variable ([example][ex-requir]).
  - [X] New dependencies are only imported inside functions that use them ([example][ex-import]).
  - [X] New or updated dependencies have been added to `requirements_all.txt` by running `script/gen_requirements_all.py`.
  - [X] New files were added to `.coveragerc`.

If the code does not interact with devices:
  - [ ] Tests have been added to verify that the new code works.

[ex-requir]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L14
[ex-import]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L23


Co-authored-by: Andrew Sayre <6730289+andrewsayre@users.noreply.github.com>
This commit is contained in:
Andreas Rydbrink 2019-03-29 03:03:02 +01:00 committed by Robbie Trencheny
parent 709419e465
commit 01052f516b
4 changed files with 211 additions and 0 deletions

View file

@ -235,11 +235,15 @@ omit =
homeassistant/components/harmony/remote.py
homeassistant/components/haveibeenpwned/sensor.py
homeassistant/components/hdmi_cec/*
<<<<<<< HEAD
homeassistant/components/heatmiser/climate.py
homeassistant/components/hikvision/binary_sensor.py
homeassistant/components/hikvisioncam/switch.py
homeassistant/components/hipchat/notify.py
homeassistant/components/hitron_coda/device_tracker.py
=======
homeassistant/components/heos/*
>>>>>>> Update HEOS to support multiple speaker and conformance.
homeassistant/components/hive/*
homeassistant/components/hlk_sw16/*
homeassistant/components/homekit_controller/*

View file

@ -0,0 +1,52 @@
"""Denon HEOS Media Player."""
import asyncio
import logging
import voluptuous as vol
from homeassistant.components.media_player.const import (
DOMAIN as MEDIA_PLAYER_DOMAIN)
from homeassistant.const import CONF_HOST, EVENT_HOMEASSISTANT_STOP
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.discovery import async_load_platform
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
DOMAIN = 'heos'
REQUIREMENTS = ['aioheos==0.4.0']
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_HOST): cv.string
})
}, extra=vol.ALLOW_EXTRA)
_LOGGER = logging.getLogger(__name__)
async def async_setup(hass: HomeAssistantType, config: ConfigType):
"""Set up the HEOS component."""
from aioheos import AioHeosController
host = config[DOMAIN][CONF_HOST]
controller = AioHeosController(hass.loop, host)
try:
await asyncio.wait_for(controller.connect(), timeout=5.0)
except asyncio.TimeoutError:
_LOGGER.error('Timeout during setup.')
return False
async def controller_close(event):
"""Close connection when HASS shutsdown."""
await controller.close()
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, controller_close)
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][MEDIA_PLAYER_DOMAIN] = controller
hass.async_create_task(async_load_platform(
hass, MEDIA_PLAYER_DOMAIN, DOMAIN, {}, config))
return True

View file

@ -0,0 +1,152 @@
"""Denon HEOS Media Player."""
from homeassistant.components.media_player import MediaPlayerDevice
from homeassistant.components.media_player.const import (
DOMAIN, MEDIA_TYPE_MUSIC, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY,
SUPPORT_PLAY_MEDIA, SUPPORT_PREVIOUS_TRACK, SUPPORT_STOP,
SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_STEP)
from homeassistant.const import STATE_IDLE, STATE_PAUSED, STATE_PLAYING
from . import DOMAIN as HEOS_DOMAIN
DEPENDENCIES = ["heos"]
SUPPORT_HEOS = (
SUPPORT_PLAY
| SUPPORT_STOP
| SUPPORT_PAUSE
| SUPPORT_PLAY_MEDIA
| SUPPORT_PREVIOUS_TRACK
| SUPPORT_NEXT_TRACK
| SUPPORT_VOLUME_MUTE
| SUPPORT_VOLUME_SET
| SUPPORT_VOLUME_STEP
)
PLAY_STATE_TO_STATE = {
"play": STATE_PLAYING,
"pause": STATE_PAUSED,
"stop": STATE_IDLE,
}
async def async_setup_platform(hass, config, async_add_devices,
discover_info=None):
"""Set up the HEOS platform."""
controller = hass.data[HEOS_DOMAIN][DOMAIN]
players = controller.get_players()
devices = [HeosMediaPlayer(p) for p in players]
async_add_devices(devices, True)
class HeosMediaPlayer(MediaPlayerDevice):
"""The HEOS player."""
def __init__(self, player):
"""Initialize."""
self._player = player
def _update_state(self):
self.async_schedule_update_ha_state()
async def async_update(self):
"""Update the player."""
self._player.request_update()
async def async_added_to_hass(self):
"""Device added to hass."""
self._player.state_change_callback = self._update_state
@property
def unique_id(self):
"""Get unique id of the player."""
return self._player.player_id
@property
def name(self):
"""Return the name of the device."""
return self._player.name
@property
def volume_level(self):
"""Volume level of the device (0..1)."""
volume = self._player.volume
return float(volume) / 100
@property
def state(self):
"""Get state."""
return PLAY_STATE_TO_STATE.get(self._player.play_state)
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def media_content_type(self):
"""Content type of current playing media."""
return MEDIA_TYPE_MUSIC
@property
def media_artist(self):
"""Artist of current playing media."""
return self._player.media_artist
@property
def media_title(self):
"""Album name of current playing media."""
return self._player.media_title
@property
def media_album_name(self):
"""Album name of current playing media."""
return self._player.media_album
@property
def media_image_url(self):
"""Return the image url of current playing media."""
return self._player.media_image_url
@property
def media_content_id(self):
"""Return the content ID of current playing media."""
return self._player.media_id
@property
def is_volume_muted(self):
"""Boolean if volume is currently muted."""
return self._player.mute == "on"
async def async_mute_volume(self, mute):
"""Mute volume."""
self._player.set_mute(mute)
async def async_media_next_track(self):
"""Go TO next track."""
self._player.play_next()
async def async_media_previous_track(self):
"""Go TO previous track."""
self._player.play_previous()
@property
def supported_features(self):
"""Flag of media commands that are supported."""
return SUPPORT_HEOS
async def async_set_volume_level(self, volume):
"""Set volume level, range 0..1."""
self._player.set_volume(volume * 100)
async def async_media_play(self):
"""Play media player."""
self._player.play()
async def async_media_stop(self):
"""Stop media player."""
self._player.stop()
async def async_media_pause(self):
"""Pause media player."""
self._player.pause()

View file

@ -123,6 +123,9 @@ aioftp==0.12.0
# homeassistant.components.harmony.remote
aioharmony==0.1.8
# homeassistant.components.heos
aioheos==0.4.0
# homeassistant.components.emulated_hue
# homeassistant.components.http
aiohttp_cors==0.7.0