Implement soundtouch select source (#31669)

This commit is contained in:
Thomas Johanns 2020-05-12 22:56:12 +02:00 committed by GitHub
parent 3fcd7866cc
commit 8f76f59b8b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 159 additions and 4 deletions

View file

@ -2,6 +2,6 @@
"domain": "soundtouch", "domain": "soundtouch",
"name": "Bose Soundtouch", "name": "Bose Soundtouch",
"documentation": "https://www.home-assistant.io/integrations/soundtouch", "documentation": "https://www.home-assistant.io/integrations/soundtouch",
"requirements": ["libsoundtouch==0.7.2"], "requirements": ["libsoundtouch==0.8"],
"codeowners": [] "codeowners": []
} }

View file

@ -3,6 +3,7 @@ import logging
import re import re
from libsoundtouch import soundtouch_device from libsoundtouch import soundtouch_device
from libsoundtouch.utils import Source
import voluptuous as vol import voluptuous as vol
from homeassistant.components.media_player import PLATFORM_SCHEMA, MediaPlayerEntity from homeassistant.components.media_player import PLATFORM_SCHEMA, MediaPlayerEntity
@ -12,6 +13,7 @@ from homeassistant.components.media_player.const import (
SUPPORT_PLAY, SUPPORT_PLAY,
SUPPORT_PLAY_MEDIA, SUPPORT_PLAY_MEDIA,
SUPPORT_PREVIOUS_TRACK, SUPPORT_PREVIOUS_TRACK,
SUPPORT_SELECT_SOURCE,
SUPPORT_TURN_OFF, SUPPORT_TURN_OFF,
SUPPORT_TURN_ON, SUPPORT_TURN_ON,
SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_MUTE,
@ -80,6 +82,7 @@ SUPPORT_SOUNDTOUCH = (
| SUPPORT_TURN_ON | SUPPORT_TURN_ON
| SUPPORT_PLAY | SUPPORT_PLAY
| SUPPORT_PLAY_MEDIA | SUPPORT_PLAY_MEDIA
| SUPPORT_SELECT_SOURCE
) )
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
@ -234,6 +237,19 @@ class SoundTouchDevice(MediaPlayerEntity):
return MAP_STATUS.get(self._status.play_status, STATE_UNAVAILABLE) return MAP_STATUS.get(self._status.play_status, STATE_UNAVAILABLE)
@property
def source(self):
"""Name of the current input source."""
return self._status.source
@property
def source_list(self):
"""List of available input sources."""
return [
Source.AUX.value,
Source.BLUETOOTH.value,
]
@property @property
def is_volume_muted(self): def is_volume_muted(self):
"""Boolean if volume is currently muted.""" """Boolean if volume is currently muted."""
@ -357,6 +373,17 @@ class SoundTouchDevice(MediaPlayerEntity):
else: else:
_LOGGER.warning("Unable to find preset with id %s", media_id) _LOGGER.warning("Unable to find preset with id %s", media_id)
def select_source(self, source):
"""Select input source."""
if source == Source.AUX.value:
_LOGGER.debug("Selecting source AUX")
self._device.select_source_aux()
elif source == Source.BLUETOOTH.value:
_LOGGER.debug("Selecting source Bluetooth")
self._device.select_source_bluetooth()
else:
_LOGGER.warning("Source %s is not supported", source)
def create_zone(self, slaves): def create_zone(self, slaves):
""" """
Create a zone (multi-room) and play on selected devices. Create a zone (multi-room) and play on selected devices.

View file

@ -837,7 +837,7 @@ libpyvivotek==0.4.0
librouteros==3.0.0 librouteros==3.0.0
# homeassistant.components.soundtouch # homeassistant.components.soundtouch
libsoundtouch==0.7.2 libsoundtouch==0.8
# homeassistant.components.life360 # homeassistant.components.life360
life360==4.1.1 life360==4.1.1

View file

@ -356,7 +356,7 @@ libpurecool==0.6.1
librouteros==3.0.0 librouteros==3.0.0
# homeassistant.components.soundtouch # homeassistant.components.soundtouch
libsoundtouch==0.7.2 libsoundtouch==0.8
# homeassistant.components.logi_circle # homeassistant.components.logi_circle
logi_circle==0.2.2 logi_circle==0.2.2

View file

@ -11,6 +11,7 @@ from libsoundtouch.device import (
import pytest import pytest
from homeassistant.components.media_player.const import ( from homeassistant.components.media_player.const import (
ATTR_INPUT_SOURCE,
ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_CONTENT_ID,
ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_CONTENT_TYPE,
) )
@ -254,6 +255,36 @@ class MockStatusPause(Status):
self._station_name = None self._station_name = None
class MockStatusPlayingAux(Status):
"""Mock status AUX."""
def __init__(self):
"""Init the class."""
self._source = "AUX"
self._play_status = "PLAY_STATE"
self._image = "image.url"
self._artist = None
self._track = None
self._album = None
self._duration = None
self._station_name = None
class MockStatusPlayingBluetooth(Status):
"""Mock status Bluetooth."""
def __init__(self):
"""Init the class."""
self._source = "BLUETOOTH"
self._play_status = "PLAY_STATE"
self._image = "image.url"
self._artist = "artist"
self._track = "track"
self._album = "album"
self._duration = None
self._station_name = None
async def test_ensure_setup_config(mocked_status, mocked_volume, hass, one_device): async def test_ensure_setup_config(mocked_status, mocked_volume, hass, one_device):
"""Test setup OK with custom config.""" """Test setup OK with custom config."""
await setup_soundtouch( await setup_soundtouch(
@ -365,6 +396,37 @@ async def test_playing_radio(mocked_status, mocked_volume, hass, one_device):
assert entity_1_state.attributes["media_title"] == "station" assert entity_1_state.attributes["media_title"] == "station"
async def test_playing_aux(mocked_status, mocked_volume, hass, one_device):
"""Test playing AUX info."""
mocked_status.side_effect = MockStatusPlayingAux
await setup_soundtouch(hass, DEVICE_1_CONFIG)
assert one_device.call_count == 1
assert mocked_status.call_count == 2
assert mocked_volume.call_count == 2
entity_1_state = hass.states.get("media_player.soundtouch_1")
assert entity_1_state.state == STATE_PLAYING
assert entity_1_state.attributes["source"] == "AUX"
async def test_playing_bluetooth(mocked_status, mocked_volume, hass, one_device):
"""Test playing Bluetooth info."""
mocked_status.side_effect = MockStatusPlayingBluetooth
await setup_soundtouch(hass, DEVICE_1_CONFIG)
assert one_device.call_count == 1
assert mocked_status.call_count == 2
assert mocked_volume.call_count == 2
entity_1_state = hass.states.get("media_player.soundtouch_1")
assert entity_1_state.state == STATE_PLAYING
assert entity_1_state.attributes["source"] == "BLUETOOTH"
assert entity_1_state.attributes["media_track"] == "track"
assert entity_1_state.attributes["media_artist"] == "artist"
assert entity_1_state.attributes["media_album_name"] == "album"
async def test_get_volume_level(mocked_status, mocked_volume, hass, one_device): async def test_get_volume_level(mocked_status, mocked_volume, hass, one_device):
"""Test volume level.""" """Test volume level."""
mocked_volume.side_effect = MockVolume mocked_volume.side_effect = MockVolume
@ -426,7 +488,7 @@ async def test_media_commands(mocked_status, mocked_volume, hass, one_device):
assert mocked_volume.call_count == 2 assert mocked_volume.call_count == 2
entity_1_state = hass.states.get("media_player.soundtouch_1") entity_1_state = hass.states.get("media_player.soundtouch_1")
assert entity_1_state.attributes["supported_features"] == 18365 assert entity_1_state.attributes["supported_features"] == 20413
@patch("libsoundtouch.device.SoundTouchDevice.power_off") @patch("libsoundtouch.device.SoundTouchDevice.power_off")
@ -694,6 +756,72 @@ async def test_play_media_url(
mocked_play_url.assert_called_with("http://fqdn/file.mp3") mocked_play_url.assert_called_with("http://fqdn/file.mp3")
@patch("libsoundtouch.device.SoundTouchDevice.select_source_aux")
async def test_select_source_aux(
mocked_select_source_aux, mocked_status, mocked_volume, hass, one_device
):
"""Test select AUX."""
await setup_soundtouch(hass, DEVICE_1_CONFIG)
assert mocked_select_source_aux.call_count == 0
await hass.services.async_call(
"media_player",
"select_source",
{"entity_id": "media_player.soundtouch_1", ATTR_INPUT_SOURCE: "AUX"},
True,
)
assert mocked_select_source_aux.call_count == 1
@patch("libsoundtouch.device.SoundTouchDevice.select_source_bluetooth")
async def test_select_source_bluetooth(
mocked_select_source_bluetooth, mocked_status, mocked_volume, hass, one_device
):
"""Test select Bluetooth."""
await setup_soundtouch(hass, DEVICE_1_CONFIG)
assert mocked_select_source_bluetooth.call_count == 0
await hass.services.async_call(
"media_player",
"select_source",
{"entity_id": "media_player.soundtouch_1", ATTR_INPUT_SOURCE: "BLUETOOTH"},
True,
)
assert mocked_select_source_bluetooth.call_count == 1
@patch("libsoundtouch.device.SoundTouchDevice.select_source_bluetooth")
@patch("libsoundtouch.device.SoundTouchDevice.select_source_aux")
async def test_select_source_invalid_source(
mocked_select_source_aux,
mocked_select_source_bluetooth,
mocked_status,
mocked_volume,
hass,
one_device,
):
"""Test select unsupported source."""
await setup_soundtouch(hass, DEVICE_1_CONFIG)
assert mocked_select_source_aux.call_count == 0
assert mocked_select_source_bluetooth.call_count == 0
await hass.services.async_call(
"media_player",
"select_source",
{
"entity_id": "media_player.soundtouch_1",
ATTR_INPUT_SOURCE: "SOMETHING_UNSUPPORTED",
},
True,
)
assert mocked_select_source_aux.call_count == 0
assert mocked_select_source_bluetooth.call_count == 0
@patch("libsoundtouch.device.SoundTouchDevice.create_zone") @patch("libsoundtouch.device.SoundTouchDevice.create_zone")
async def test_play_everywhere( async def test_play_everywhere(
mocked_create_zone, mocked_status, mocked_volume, hass, two_zones mocked_create_zone, mocked_status, mocked_volume, hass, two_zones