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:
parent
709419e465
commit
01052f516b
4 changed files with 211 additions and 0 deletions
|
@ -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/*
|
||||
|
|
52
homeassistant/components/heos/__init__.py
Normal file
52
homeassistant/components/heos/__init__.py
Normal 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
|
152
homeassistant/components/heos/media_player.py
Normal file
152
homeassistant/components/heos/media_player.py
Normal 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()
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue