Implement soundtouch select source (#31669)
This commit is contained in:
parent
3fcd7866cc
commit
8f76f59b8b
5 changed files with 159 additions and 4 deletions
|
@ -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": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Reference in a new issue