diff --git a/homeassistant/components/soundtouch/media_player.py b/homeassistant/components/soundtouch/media_player.py
index 7c9ade3bee1..f8a5191d9db 100644
--- a/homeassistant/components/soundtouch/media_player.py
+++ b/homeassistant/components/soundtouch/media_player.py
@@ -523,7 +523,7 @@ class SoundTouchDevice(MediaPlayerEntity):
for slave in zone_slaves:
slave_instance = self._get_instance_by_ip(slave.device_ip)
- if slave_instance:
+ if slave_instance and slave_instance.entity_id != master:
slaves.append(slave_instance.entity_id)
attributes = {
diff --git a/tests/components/soundtouch/conftest.py b/tests/components/soundtouch/conftest.py
new file mode 100644
index 00000000000..dcac360d253
--- /dev/null
+++ b/tests/components/soundtouch/conftest.py
@@ -0,0 +1,286 @@
+"""Fixtures for Bose SoundTouch integration tests."""
+import pytest
+from requests_mock import Mocker
+
+from homeassistant.components.media_player.const import DOMAIN as MEDIA_PLAYER_DOMAIN
+from homeassistant.components.soundtouch.const import DOMAIN
+from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PLATFORM
+
+from tests.common import load_fixture
+
+DEVICE_1_ID = "020000000001"
+DEVICE_2_ID = "020000000002"
+DEVICE_1_IP = "192.168.42.1"
+DEVICE_2_IP = "192.168.42.2"
+DEVICE_1_URL = f"http://{DEVICE_1_IP}:8090"
+DEVICE_2_URL = f"http://{DEVICE_2_IP}:8090"
+DEVICE_1_NAME = "My Soundtouch 1"
+DEVICE_2_NAME = "My Soundtouch 2"
+DEVICE_1_ENTITY_ID = f"{MEDIA_PLAYER_DOMAIN}.my_soundtouch_1"
+DEVICE_2_ENTITY_ID = f"{MEDIA_PLAYER_DOMAIN}.my_soundtouch_2"
+
+
+# pylint: disable=redefined-outer-name
+
+
+@pytest.fixture
+def device1_config() -> dict[str, str]:
+ """Mock SoundTouch device 1 config."""
+ yield {CONF_PLATFORM: DOMAIN, CONF_HOST: DEVICE_1_IP, CONF_NAME: DEVICE_1_NAME}
+
+
+@pytest.fixture
+def device2_config() -> dict[str, str]:
+ """Mock SoundTouch device 2 config."""
+ yield {CONF_PLATFORM: DOMAIN, CONF_HOST: DEVICE_2_IP, CONF_NAME: DEVICE_2_NAME}
+
+
+@pytest.fixture(scope="session")
+def device1_info() -> str:
+ """Load SoundTouch device 1 info response and return it."""
+ return load_fixture("soundtouch/device1_info.xml")
+
+
+@pytest.fixture(scope="session")
+def device1_now_playing_aux() -> str:
+ """Load SoundTouch device 1 now_playing response and return it."""
+ return load_fixture("soundtouch/device1_now_playing_aux.xml")
+
+
+@pytest.fixture(scope="session")
+def device1_now_playing_bluetooth() -> str:
+ """Load SoundTouch device 1 now_playing response and return it."""
+ return load_fixture("soundtouch/device1_now_playing_bluetooth.xml")
+
+
+@pytest.fixture(scope="session")
+def device1_now_playing_radio() -> str:
+ """Load SoundTouch device 1 now_playing response and return it."""
+ return load_fixture("soundtouch/device1_now_playing_radio.xml")
+
+
+@pytest.fixture(scope="session")
+def device1_now_playing_standby() -> str:
+ """Load SoundTouch device 1 now_playing response and return it."""
+ return load_fixture("soundtouch/device1_now_playing_standby.xml")
+
+
+@pytest.fixture(scope="session")
+def device1_now_playing_upnp() -> str:
+ """Load SoundTouch device 1 now_playing response and return it."""
+ return load_fixture("soundtouch/device1_now_playing_upnp.xml")
+
+
+@pytest.fixture(scope="session")
+def device1_now_playing_upnp_paused() -> str:
+ """Load SoundTouch device 1 now_playing response and return it."""
+ return load_fixture("soundtouch/device1_now_playing_upnp_paused.xml")
+
+
+@pytest.fixture(scope="session")
+def device1_presets() -> str:
+ """Load SoundTouch device 1 presets response and return it."""
+ return load_fixture("soundtouch/device1_presets.xml")
+
+
+@pytest.fixture(scope="session")
+def device1_volume() -> str:
+ """Load SoundTouch device 1 volume response and return it."""
+ return load_fixture("soundtouch/device1_volume.xml")
+
+
+@pytest.fixture(scope="session")
+def device1_volume_muted() -> str:
+ """Load SoundTouch device 1 volume response and return it."""
+ return load_fixture("soundtouch/device1_volume_muted.xml")
+
+
+@pytest.fixture(scope="session")
+def device1_zone_master() -> str:
+ """Load SoundTouch device 1 getZone response and return it."""
+ return load_fixture("soundtouch/device1_getZone_master.xml")
+
+
+@pytest.fixture(scope="session")
+def device2_info() -> str:
+ """Load SoundTouch device 2 info response and return it."""
+ return load_fixture("soundtouch/device2_info.xml")
+
+
+@pytest.fixture(scope="session")
+def device2_volume() -> str:
+ """Load SoundTouch device 2 volume response and return it."""
+ return load_fixture("soundtouch/device2_volume.xml")
+
+
+@pytest.fixture(scope="session")
+def device2_now_playing_standby() -> str:
+ """Load SoundTouch device 2 now_playing response and return it."""
+ return load_fixture("soundtouch/device2_now_playing_standby.xml")
+
+
+@pytest.fixture(scope="session")
+def device2_zone_slave() -> str:
+ """Load SoundTouch device 2 getZone response and return it."""
+ return load_fixture("soundtouch/device2_getZone_slave.xml")
+
+
+@pytest.fixture(scope="session")
+def zone_empty() -> str:
+ """Load empty SoundTouch getZone response and return it."""
+ return load_fixture("soundtouch/getZone_empty.xml")
+
+
+@pytest.fixture
+def device1_requests_mock(
+ requests_mock: Mocker,
+ device1_info: str,
+ device1_volume: str,
+ device1_presets: str,
+ device1_zone_master: str,
+) -> Mocker:
+ """Mock SoundTouch device 1 API - base URLs."""
+ requests_mock.get(f"{DEVICE_1_URL}/info", text=device1_info)
+ requests_mock.get(f"{DEVICE_1_URL}/volume", text=device1_volume)
+ requests_mock.get(f"{DEVICE_1_URL}/presets", text=device1_presets)
+ requests_mock.get(f"{DEVICE_1_URL}/getZone", text=device1_zone_master)
+ yield requests_mock
+
+
+@pytest.fixture
+def device1_requests_mock_standby(
+ device1_requests_mock: Mocker,
+ device1_now_playing_standby: str,
+):
+ """Mock SoundTouch device 1 API - standby."""
+ device1_requests_mock.get(
+ f"{DEVICE_1_URL}/now_playing", text=device1_now_playing_standby
+ )
+
+
+@pytest.fixture
+def device1_requests_mock_aux(
+ device1_requests_mock: Mocker,
+ device1_now_playing_aux: str,
+):
+ """Mock SoundTouch device 1 API - playing AUX."""
+ device1_requests_mock.get(
+ f"{DEVICE_1_URL}/now_playing", text=device1_now_playing_aux
+ )
+
+
+@pytest.fixture
+def device1_requests_mock_bluetooth(
+ device1_requests_mock: Mocker,
+ device1_now_playing_bluetooth: str,
+):
+ """Mock SoundTouch device 1 API - playing bluetooth."""
+ device1_requests_mock.get(
+ f"{DEVICE_1_URL}/now_playing", text=device1_now_playing_bluetooth
+ )
+
+
+@pytest.fixture
+def device1_requests_mock_radio(
+ device1_requests_mock: Mocker,
+ device1_now_playing_radio: str,
+):
+ """Mock SoundTouch device 1 API - playing radio."""
+ device1_requests_mock.get(
+ f"{DEVICE_1_URL}/now_playing", text=device1_now_playing_radio
+ )
+
+
+@pytest.fixture
+def device1_requests_mock_upnp(
+ device1_requests_mock: Mocker,
+ device1_now_playing_upnp: str,
+):
+ """Mock SoundTouch device 1 API - playing UPNP."""
+ device1_requests_mock.get(
+ f"{DEVICE_1_URL}/now_playing", text=device1_now_playing_upnp
+ )
+
+
+@pytest.fixture
+def device1_requests_mock_upnp_paused(
+ device1_requests_mock: Mocker,
+ device1_now_playing_upnp_paused: str,
+):
+ """Mock SoundTouch device 1 API - playing UPNP (paused)."""
+ device1_requests_mock.get(
+ f"{DEVICE_1_URL}/now_playing", text=device1_now_playing_upnp_paused
+ )
+
+
+@pytest.fixture
+def device1_requests_mock_key(
+ device1_requests_mock: Mocker,
+):
+ """Mock SoundTouch device 1 API - key endpoint."""
+ yield device1_requests_mock.post(f"{DEVICE_1_URL}/key")
+
+
+@pytest.fixture
+def device1_requests_mock_volume(
+ device1_requests_mock: Mocker,
+):
+ """Mock SoundTouch device 1 API - volume endpoint."""
+ yield device1_requests_mock.post(f"{DEVICE_1_URL}/volume")
+
+
+@pytest.fixture
+def device1_requests_mock_select(
+ device1_requests_mock: Mocker,
+):
+ """Mock SoundTouch device 1 API - select endpoint."""
+ yield device1_requests_mock.post(f"{DEVICE_1_URL}/select")
+
+
+@pytest.fixture
+def device1_requests_mock_set_zone(
+ device1_requests_mock: Mocker,
+):
+ """Mock SoundTouch device 1 API - setZone endpoint."""
+ yield device1_requests_mock.post(f"{DEVICE_1_URL}/setZone")
+
+
+@pytest.fixture
+def device1_requests_mock_add_zone_slave(
+ device1_requests_mock: Mocker,
+):
+ """Mock SoundTouch device 1 API - addZoneSlave endpoint."""
+ yield device1_requests_mock.post(f"{DEVICE_1_URL}/addZoneSlave")
+
+
+@pytest.fixture
+def device1_requests_mock_remove_zone_slave(
+ device1_requests_mock: Mocker,
+):
+ """Mock SoundTouch device 1 API - removeZoneSlave endpoint."""
+ yield device1_requests_mock.post(f"{DEVICE_1_URL}/removeZoneSlave")
+
+
+@pytest.fixture
+def device1_requests_mock_dlna(
+ device1_requests_mock: Mocker,
+):
+ """Mock SoundTouch device 1 API - DLNA endpoint."""
+ yield device1_requests_mock.post(f"http://{DEVICE_1_IP}:8091/AVTransport/Control")
+
+
+@pytest.fixture
+def device2_requests_mock_standby(
+ requests_mock: Mocker,
+ device2_info: str,
+ device2_volume: str,
+ device2_now_playing_standby: str,
+ device2_zone_slave: str,
+) -> Mocker:
+ """Mock SoundTouch device 2 API."""
+ requests_mock.get(f"{DEVICE_2_URL}/info", text=device2_info)
+ requests_mock.get(f"{DEVICE_2_URL}/volume", text=device2_volume)
+ requests_mock.get(f"{DEVICE_2_URL}/now_playing", text=device2_now_playing_standby)
+ requests_mock.get(f"{DEVICE_2_URL}/getZone", text=device2_zone_slave)
+
+ yield requests_mock
diff --git a/tests/components/soundtouch/fixtures/device1_getZone_master.xml b/tests/components/soundtouch/fixtures/device1_getZone_master.xml
new file mode 100644
index 00000000000..f4b0fd05a51
--- /dev/null
+++ b/tests/components/soundtouch/fixtures/device1_getZone_master.xml
@@ -0,0 +1,6 @@
+
+
+ 020000000001
+ 020000000002
+ 020000000003
+
\ No newline at end of file
diff --git a/tests/components/soundtouch/fixtures/device1_info.xml b/tests/components/soundtouch/fixtures/device1_info.xml
new file mode 100644
index 00000000000..27878969ca0
--- /dev/null
+++ b/tests/components/soundtouch/fixtures/device1_info.xml
@@ -0,0 +1,32 @@
+
+
+ My SoundTouch 1
+ SoundTouch 10
+ 0
+
+
+ SCM
+ 27.0.3.46298.4608935 epdbuild.trunk.hepdswbld04.2021-10-06T16:35:02
+ P0000000000000000000001
+
+
+ PackagedProduct
+ 27.0.3.46298.4608935 epdbuild.trunk.hepdswbld04.2021-10-06T16:35:02
+ 000000P00000001AE
+
+
+ https://streaming.bose.com
+
+ 020000000001
+ 192.168.42.1
+
+
+ 060000000001
+ 192.168.42.1
+
+ sm2
+ rhino
+ normal
+ US
+ US
+
\ No newline at end of file
diff --git a/tests/components/soundtouch/fixtures/device1_now_playing_aux.xml b/tests/components/soundtouch/fixtures/device1_now_playing_aux.xml
new file mode 100644
index 00000000000..e19dc1dd954
--- /dev/null
+++ b/tests/components/soundtouch/fixtures/device1_now_playing_aux.xml
@@ -0,0 +1,7 @@
+
+
+
+ AUX IN
+
+ PLAY_STATE
+
\ No newline at end of file
diff --git a/tests/components/soundtouch/fixtures/device1_now_playing_bluetooth.xml b/tests/components/soundtouch/fixtures/device1_now_playing_bluetooth.xml
new file mode 100644
index 00000000000..c43fe187f2f
--- /dev/null
+++ b/tests/components/soundtouch/fixtures/device1_now_playing_bluetooth.xml
@@ -0,0 +1,16 @@
+
+
+
+ MockPairedBluetoothDevice
+
+
+ MockArtist
+ MockAlbum
+ MockPairedBluetoothDevice
+
+
+ PLAY_STATE
+
+
+
+
\ No newline at end of file
diff --git a/tests/components/soundtouch/fixtures/device1_now_playing_radio.xml b/tests/components/soundtouch/fixtures/device1_now_playing_radio.xml
new file mode 100644
index 00000000000..b9d47216b3a
--- /dev/null
+++ b/tests/components/soundtouch/fixtures/device1_now_playing_radio.xml
@@ -0,0 +1,16 @@
+
+
+
+ MockStation
+ http://cdn-profiles.tunein.com/sXXXXX/images/logoq.png
+
+
+ MockArtist
+ MockAlbum
+ MockStation
+ http://cdn-profiles.tunein.com/sXXXXX/images/logoq.png
+
+ PLAY_STATE
+ RADIO_STREAMING
+
+
\ No newline at end of file
diff --git a/tests/components/soundtouch/fixtures/device1_now_playing_standby.xml b/tests/components/soundtouch/fixtures/device1_now_playing_standby.xml
new file mode 100644
index 00000000000..67acae6a0ef
--- /dev/null
+++ b/tests/components/soundtouch/fixtures/device1_now_playing_standby.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/tests/components/soundtouch/fixtures/device1_now_playing_upnp.xml b/tests/components/soundtouch/fixtures/device1_now_playing_upnp.xml
new file mode 100644
index 00000000000..e58e62072ce
--- /dev/null
+++ b/tests/components/soundtouch/fixtures/device1_now_playing_upnp.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ MockArtist
+ MockAlbum
+
+
+
+ PLAY_STATE
+
+ TRACK_ONDEMAND
+
\ No newline at end of file
diff --git a/tests/components/soundtouch/fixtures/device1_now_playing_upnp_paused.xml b/tests/components/soundtouch/fixtures/device1_now_playing_upnp_paused.xml
new file mode 100644
index 00000000000..6275ada6e4b
--- /dev/null
+++ b/tests/components/soundtouch/fixtures/device1_now_playing_upnp_paused.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ MockArtist
+ MockAlbum
+
+
+
+ PAUSE_STATE
+
+ TRACK_ONDEMAND
+
\ No newline at end of file
diff --git a/tests/components/soundtouch/fixtures/device1_presets.xml b/tests/components/soundtouch/fixtures/device1_presets.xml
new file mode 100644
index 00000000000..6bacfa48732
--- /dev/null
+++ b/tests/components/soundtouch/fixtures/device1_presets.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+ MockStation
+ http://cdn-profiles.tunein.com/sXXXXX/images/logoq.png
+
+
+
\ No newline at end of file
diff --git a/tests/components/soundtouch/fixtures/device1_volume.xml b/tests/components/soundtouch/fixtures/device1_volume.xml
new file mode 100644
index 00000000000..cef90efa37d
--- /dev/null
+++ b/tests/components/soundtouch/fixtures/device1_volume.xml
@@ -0,0 +1,6 @@
+
+
+ 12
+ 12
+ false
+
\ No newline at end of file
diff --git a/tests/components/soundtouch/fixtures/device1_volume_muted.xml b/tests/components/soundtouch/fixtures/device1_volume_muted.xml
new file mode 100644
index 00000000000..e26fbd55e08
--- /dev/null
+++ b/tests/components/soundtouch/fixtures/device1_volume_muted.xml
@@ -0,0 +1,6 @@
+
+
+ 12
+ 12
+ true
+
\ No newline at end of file
diff --git a/tests/components/soundtouch/fixtures/device2_getZone_slave.xml b/tests/components/soundtouch/fixtures/device2_getZone_slave.xml
new file mode 100644
index 00000000000..fa9db0bf748
--- /dev/null
+++ b/tests/components/soundtouch/fixtures/device2_getZone_slave.xml
@@ -0,0 +1,4 @@
+
+
+ 020000000002
+
\ No newline at end of file
diff --git a/tests/components/soundtouch/fixtures/device2_info.xml b/tests/components/soundtouch/fixtures/device2_info.xml
new file mode 100644
index 00000000000..a93a19fb52a
--- /dev/null
+++ b/tests/components/soundtouch/fixtures/device2_info.xml
@@ -0,0 +1,32 @@
+
+
+ My SoundTouch 2
+ SoundTouch 10
+ 0
+
+
+ SCM
+ 27.0.3.46298.4608935 epdbuild.trunk.hepdswbld04.2021-10-06T16:35:02
+ P0000000000000000000002
+
+
+ PackagedProduct
+ 27.0.3.46298.4608935 epdbuild.trunk.hepdswbld04.2021-10-06T16:35:02
+ 000000P00000002AE
+
+
+ https://streaming.bose.com
+
+ 020000000002
+ 192.168.42.2
+
+
+ 060000000002
+ 192.168.42.2
+
+ sm2
+ rhino
+ normal
+ US
+ US
+
\ No newline at end of file
diff --git a/tests/components/soundtouch/fixtures/device2_now_playing_standby.xml b/tests/components/soundtouch/fixtures/device2_now_playing_standby.xml
new file mode 100644
index 00000000000..1b8bf8a5a3c
--- /dev/null
+++ b/tests/components/soundtouch/fixtures/device2_now_playing_standby.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/tests/components/soundtouch/fixtures/device2_volume.xml b/tests/components/soundtouch/fixtures/device2_volume.xml
new file mode 100644
index 00000000000..436bd888980
--- /dev/null
+++ b/tests/components/soundtouch/fixtures/device2_volume.xml
@@ -0,0 +1,6 @@
+
+
+ 10
+ 10
+ false
+
\ No newline at end of file
diff --git a/tests/components/soundtouch/test_media_player.py b/tests/components/soundtouch/test_media_player.py
index 797b5b440d1..1b16508bb88 100644
--- a/tests/components/soundtouch/test_media_player.py
+++ b/tests/components/soundtouch/test_media_player.py
@@ -1,1062 +1,686 @@
-"""Test the Soundtouch component."""
-from unittest.mock import call, patch
+"""Test the SoundTouch component."""
+from typing import Any
-from libsoundtouch.device import (
- Config,
- Preset,
- SoundTouchDevice as STD,
- Status,
- Volume,
- ZoneSlave,
- ZoneStatus,
-)
-import pytest
+from requests_mock import Mocker
from homeassistant.components.media_player.const import (
ATTR_INPUT_SOURCE,
+ ATTR_MEDIA_ALBUM_NAME,
+ ATTR_MEDIA_ARTIST,
ATTR_MEDIA_CONTENT_ID,
ATTR_MEDIA_CONTENT_TYPE,
+ ATTR_MEDIA_DURATION,
+ ATTR_MEDIA_TITLE,
+ ATTR_MEDIA_TRACK,
+ ATTR_MEDIA_VOLUME_MUTED,
+ DOMAIN as MEDIA_PLAYER_DOMAIN,
+)
+from homeassistant.components.soundtouch.const import (
+ DOMAIN,
+ SERVICE_ADD_ZONE_SLAVE,
+ SERVICE_CREATE_ZONE,
+ SERVICE_PLAY_EVERYWHERE,
+ SERVICE_REMOVE_ZONE_SLAVE,
)
-from homeassistant.components.soundtouch import media_player as soundtouch
-from homeassistant.components.soundtouch.const import DOMAIN
from homeassistant.components.soundtouch.media_player import (
ATTR_SOUNDTOUCH_GROUP,
ATTR_SOUNDTOUCH_ZONE,
DATA_SOUNDTOUCH,
)
from homeassistant.const import STATE_OFF, STATE_PAUSED, STATE_PLAYING
-from homeassistant.helpers.discovery import async_load_platform
+from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
-# pylint: disable=super-init-not-called
+from .conftest import DEVICE_1_ENTITY_ID, DEVICE_2_ENTITY_ID
-DEVICE_1_IP = "192.168.0.1"
-DEVICE_2_IP = "192.168.0.2"
-DEVICE_1_ID = 1
-DEVICE_2_ID = 2
-
-
-def get_config(host=DEVICE_1_IP, port=8090, name="soundtouch"):
- """Return a default component."""
- return {"platform": DOMAIN, "host": host, "port": port, "name": name}
-
-
-DEVICE_1_CONFIG = {**get_config(), "name": "soundtouch_1"}
-DEVICE_2_CONFIG = {**get_config(), "host": DEVICE_2_IP, "name": "soundtouch_2"}
-
-
-@pytest.fixture(name="one_device")
-def one_device_fixture():
- """Mock one master device."""
- device_1 = MockDevice()
- device_patch = patch(
- "homeassistant.components.soundtouch.media_player.soundtouch_device",
- return_value=device_1,
+async def setup_soundtouch(hass: HomeAssistant, *configs: dict[str, str]):
+ """Initialize media_player for tests."""
+ assert await async_setup_component(
+ hass, MEDIA_PLAYER_DOMAIN, {MEDIA_PLAYER_DOMAIN: list(configs)}
)
- with device_patch as device:
- yield device
-
-
-@pytest.fixture(name="two_zones")
-def two_zones_fixture():
- """Mock one master and one slave."""
- device_1 = MockDevice(
- DEVICE_1_ID,
- MockZoneStatus(
- is_master=True,
- master_id=DEVICE_1_ID,
- master_ip=DEVICE_1_IP,
- slaves=[MockZoneSlave(DEVICE_2_IP)],
- ),
- )
- device_2 = MockDevice(
- DEVICE_2_ID,
- MockZoneStatus(
- is_master=False,
- master_id=DEVICE_1_ID,
- master_ip=DEVICE_1_IP,
- slaves=[MockZoneSlave(DEVICE_2_IP)],
- ),
- )
- devices = {DEVICE_1_IP: device_1, DEVICE_2_IP: device_2}
- device_patch = patch(
- "homeassistant.components.soundtouch.media_player.soundtouch_device",
- side_effect=lambda host, _: devices[host],
- )
- with device_patch as device:
- yield device
-
-
-@pytest.fixture(name="mocked_status")
-def status_fixture():
- """Mock the device status."""
- status_patch = patch(
- "libsoundtouch.device.SoundTouchDevice.status", side_effect=MockStatusPlaying
- )
- with status_patch as status:
- yield status
-
-
-@pytest.fixture(name="mocked_volume")
-def volume_fixture():
- """Mock the device volume."""
- volume_patch = patch("libsoundtouch.device.SoundTouchDevice.volume")
- with volume_patch as volume:
- yield volume
-
-
-async def setup_soundtouch(hass, config):
- """Set up soundtouch integration."""
- assert await async_setup_component(hass, "media_player", {"media_player": config})
await hass.async_block_till_done()
await hass.async_start()
-class MockDevice(STD):
- """Mock device."""
-
- def __init__(self, id=None, zone_status=None):
- """Init the class."""
- self._config = MockConfig(id)
- self._zone_status = zone_status or MockZoneStatus()
-
- def zone_status(self, refresh=True):
- """Zone status mock object."""
- return self._zone_status
-
-
-class MockConfig(Config):
- """Mock config."""
-
- def __init__(self, id=None):
- """Init class."""
- self._name = "name"
- self._id = id or DEVICE_1_ID
-
-
-class MockZoneStatus(ZoneStatus):
- """Mock zone status."""
-
- def __init__(self, is_master=True, master_id=None, master_ip=None, slaves=None):
- """Init the class."""
- self._is_master = is_master
- self._master_id = master_id
- self._master_ip = master_ip
- self._slaves = slaves or []
-
-
-class MockZoneSlave(ZoneSlave):
- """Mock zone slave."""
-
- def __init__(self, device_ip=None, role=None):
- """Init the class."""
- self._ip = device_ip
- self._role = role
-
-
-def _mocked_presets(*args, **kwargs):
- """Return a list of mocked presets."""
- return [MockPreset("1")]
-
-
-class MockPreset(Preset):
- """Mock preset."""
-
- def __init__(self, id_):
- """Init the class."""
- self._id = id_
- self._name = "preset"
-
-
-class MockVolume(Volume):
- """Mock volume with value."""
-
- def __init__(self):
- """Init class."""
- self._actual = 12
- self._muted = False
-
-
-class MockVolumeMuted(Volume):
- """Mock volume muted."""
-
- def __init__(self):
- """Init the class."""
- self._actual = 12
- self._muted = True
-
-
-class MockStatusStandby(Status):
- """Mock status standby."""
-
- def __init__(self):
- """Init the class."""
- self._source = "STANDBY"
-
-
-class MockStatusPlaying(Status):
- """Mock status playing media."""
-
- def __init__(self):
- """Init the class."""
- self._source = ""
- self._play_status = "PLAY_STATE"
- self._image = "image.url"
- self._artist = "artist"
- self._track = "track"
- self._album = "album"
- self._duration = 1
- self._station_name = None
-
-
-class MockStatusPlayingRadio(Status):
- """Mock status radio."""
-
- def __init__(self):
- """Init the class."""
- self._source = ""
- self._play_status = "PLAY_STATE"
- self._image = "image.url"
- self._artist = None
- self._track = None
- self._album = None
- self._duration = None
- self._station_name = "station"
-
-
-class MockStatusUnknown(Status):
- """Mock status unknown media."""
-
- def __init__(self):
- """Init the class."""
- self._source = ""
- 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 MockStatusPause(Status):
- """Mock status pause."""
-
- def __init__(self):
- """Init the class."""
- self._source = ""
- self._play_status = "PAUSE_STATE"
- self._image = "image.url"
- self._artist = None
- self._track = None
- self._album = None
- self._duration = 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):
- """Test setup OK with custom config."""
- await setup_soundtouch(
- hass, get_config(host="192.168.1.44", port=8888, name="custom_sound")
- )
-
- assert one_device.call_count == 1
- assert one_device.call_args == call("192.168.1.44", 8888)
- assert len(hass.states.async_all()) == 1
- state = hass.states.get("media_player.custom_sound")
- assert state.name == "custom_sound"
-
-
-async def test_ensure_setup_discovery(mocked_status, mocked_volume, hass, one_device):
- """Test setup with discovery."""
- new_device = {
- "port": "8090",
- "host": "192.168.1.1",
- "properties": {},
- "hostname": "hostname.local",
- }
- await async_load_platform(
- hass, "media_player", DOMAIN, new_device, {"media_player": {}}
- )
- await hass.async_block_till_done()
-
- assert one_device.call_count == 1
- assert one_device.call_args == call("192.168.1.1", 8090)
- assert len(hass.states.async_all()) == 1
-
-
-async def test_ensure_setup_discovery_no_duplicate(
- mocked_status, mocked_volume, hass, one_device
+async def _test_key_service(
+ hass: HomeAssistant,
+ requests_mock_key,
+ service: str,
+ service_data: dict[str, Any],
+ key_name: str,
):
- """Test setup OK if device already exists."""
- await setup_soundtouch(hass, DEVICE_1_CONFIG)
-
- assert one_device.call_count == 1
- assert len(hass.states.async_all()) == 1
-
- new_device = {
- "port": "8090",
- "host": "192.168.1.1",
- "properties": {},
- "hostname": "hostname.local",
- }
- await async_load_platform(
- hass, "media_player", DOMAIN, new_device, {"media_player": DEVICE_1_CONFIG}
- )
- await hass.async_block_till_done()
- assert one_device.call_count == 2
- assert len(hass.states.async_all()) == 2
-
- existing_device = {
- "port": "8090",
- "host": "192.168.0.1",
- "properties": {},
- "hostname": "hostname.local",
- }
- await async_load_platform(
- hass, "media_player", DOMAIN, existing_device, {"media_player": DEVICE_1_CONFIG}
- )
- await hass.async_block_till_done()
- assert one_device.call_count == 2
- assert len(hass.states.async_all()) == 2
+ """Test API calls that use the /key endpoint to emulate physical button clicks."""
+ requests_mock_key.reset()
+ await hass.services.async_call("media_player", service, service_data, True)
+ assert requests_mock_key.call_count == 2
+ assert f">{key_name}" in requests_mock_key.last_request.text
-async def test_playing_media(mocked_status, mocked_volume, hass, one_device):
+async def test_playing_media(
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device1_requests_mock_upnp,
+):
"""Test playing media info."""
- await setup_soundtouch(hass, DEVICE_1_CONFIG)
+ await setup_soundtouch(hass, device1_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["media_title"] == "artist - track"
- 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"
- assert entity_1_state.attributes["media_duration"] == 1
+ entity_state = hass.states.get(DEVICE_1_ENTITY_ID)
+ assert entity_state.state == STATE_PLAYING
+ assert entity_state.attributes[ATTR_MEDIA_TITLE] == "MockArtist - MockTrack"
+ assert entity_state.attributes[ATTR_MEDIA_TRACK] == "MockTrack"
+ assert entity_state.attributes[ATTR_MEDIA_ARTIST] == "MockArtist"
+ assert entity_state.attributes[ATTR_MEDIA_ALBUM_NAME] == "MockAlbum"
+ assert entity_state.attributes[ATTR_MEDIA_DURATION] == 42
-async def test_playing_unknown_media(mocked_status, mocked_volume, hass, one_device):
- """Test playing media info."""
- mocked_status.side_effect = MockStatusUnknown
- 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
-
-
-async def test_playing_radio(mocked_status, mocked_volume, hass, one_device):
+async def test_playing_radio(
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device1_requests_mock_radio,
+):
"""Test playing radio info."""
- mocked_status.side_effect = MockStatusPlayingRadio
- await setup_soundtouch(hass, DEVICE_1_CONFIG)
+ await setup_soundtouch(hass, device1_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["media_title"] == "station"
+ entity_state = hass.states.get(DEVICE_1_ENTITY_ID)
+ assert entity_state.state == STATE_PLAYING
+ assert entity_state.attributes[ATTR_MEDIA_TITLE] == "MockStation"
-async def test_playing_aux(mocked_status, mocked_volume, hass, one_device):
+async def test_playing_aux(
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device1_requests_mock_aux,
+):
"""Test playing AUX info."""
- mocked_status.side_effect = MockStatusPlayingAux
- await setup_soundtouch(hass, DEVICE_1_CONFIG)
+ await setup_soundtouch(hass, device1_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"
+ entity_state = hass.states.get(DEVICE_1_ENTITY_ID)
+ assert entity_state.state == STATE_PLAYING
+ assert entity_state.attributes[ATTR_INPUT_SOURCE] == "AUX"
-async def test_playing_bluetooth(mocked_status, mocked_volume, hass, one_device):
+async def test_playing_bluetooth(
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device1_requests_mock_bluetooth,
+):
"""Test playing Bluetooth info."""
- mocked_status.side_effect = MockStatusPlayingBluetooth
- await setup_soundtouch(hass, DEVICE_1_CONFIG)
+ await setup_soundtouch(hass, device1_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"
+ entity_state = hass.states.get(DEVICE_1_ENTITY_ID)
+ assert entity_state.state == STATE_PLAYING
+ assert entity_state.attributes[ATTR_INPUT_SOURCE] == "BLUETOOTH"
+ assert entity_state.attributes[ATTR_MEDIA_TRACK] == "MockTrack"
+ assert entity_state.attributes[ATTR_MEDIA_ARTIST] == "MockArtist"
+ assert entity_state.attributes[ATTR_MEDIA_ALBUM_NAME] == "MockAlbum"
-async def test_get_volume_level(mocked_status, mocked_volume, hass, one_device):
+async def test_get_volume_level(
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device1_requests_mock_upnp,
+):
"""Test volume level."""
- mocked_volume.side_effect = MockVolume
- await setup_soundtouch(hass, DEVICE_1_CONFIG)
+ await setup_soundtouch(hass, device1_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.attributes["volume_level"] == 0.12
+ entity_state = hass.states.get(DEVICE_1_ENTITY_ID)
+ assert entity_state.attributes["volume_level"] == 0.12
-async def test_get_state_off(mocked_status, mocked_volume, hass, one_device):
+async def test_get_state_off(
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device1_requests_mock_standby,
+):
"""Test state device is off."""
- mocked_status.side_effect = MockStatusStandby
- await setup_soundtouch(hass, DEVICE_1_CONFIG)
+ await setup_soundtouch(hass, device1_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_OFF
+ entity_state = hass.states.get(DEVICE_1_ENTITY_ID)
+ assert entity_state.state == STATE_OFF
-async def test_get_state_pause(mocked_status, mocked_volume, hass, one_device):
+async def test_get_state_pause(
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device1_requests_mock_upnp_paused,
+):
"""Test state device is paused."""
- mocked_status.side_effect = MockStatusPause
- await setup_soundtouch(hass, DEVICE_1_CONFIG)
+ await setup_soundtouch(hass, device1_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_PAUSED
+ entity_state = hass.states.get(DEVICE_1_ENTITY_ID)
+ assert entity_state.state == STATE_PAUSED
-async def test_is_muted(mocked_status, mocked_volume, hass, one_device):
+async def test_is_muted(
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device1_requests_mock_upnp,
+ device1_volume_muted: str,
+):
"""Test device volume is muted."""
- mocked_volume.side_effect = MockVolumeMuted
- await setup_soundtouch(hass, DEVICE_1_CONFIG)
+ with Mocker(real_http=True) as mocker:
+ mocker.get("/volume", text=device1_volume_muted)
- assert one_device.call_count == 1
- assert mocked_status.call_count == 2
- assert mocked_volume.call_count == 2
+ await setup_soundtouch(hass, device1_config)
- entity_1_state = hass.states.get("media_player.soundtouch_1")
- assert entity_1_state.attributes["is_volume_muted"]
+ entity_state = hass.states.get(DEVICE_1_ENTITY_ID)
+ assert entity_state.attributes[ATTR_MEDIA_VOLUME_MUTED]
-async def test_media_commands(mocked_status, mocked_volume, hass, one_device):
- """Test supported media commands."""
- 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.attributes["supported_features"] == 151485
-
-
-@patch("libsoundtouch.device.SoundTouchDevice.power_off")
async def test_should_turn_off(
- mocked_power_off, mocked_status, mocked_volume, hass, one_device
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device1_requests_mock_upnp,
+ device1_requests_mock_key,
):
"""Test device is turned off."""
- 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
-
- await hass.services.async_call(
- "media_player",
+ await setup_soundtouch(hass, device1_config)
+ await _test_key_service(
+ hass,
+ device1_requests_mock_key,
"turn_off",
- {"entity_id": "media_player.soundtouch_1"},
- True,
+ {"entity_id": DEVICE_1_ENTITY_ID},
+ "POWER",
)
- assert mocked_status.call_count == 3
- assert mocked_power_off.call_count == 1
-@patch("libsoundtouch.device.SoundTouchDevice.power_on")
async def test_should_turn_on(
- mocked_power_on, mocked_status, mocked_volume, hass, one_device
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device1_requests_mock_standby,
+ device1_requests_mock_key,
):
"""Test device is turned on."""
- mocked_status.side_effect = MockStatusStandby
- 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
-
- await hass.services.async_call(
- "media_player",
+ await setup_soundtouch(hass, device1_config)
+ await _test_key_service(
+ hass,
+ device1_requests_mock_key,
"turn_on",
- {"entity_id": "media_player.soundtouch_1"},
- True,
+ {"entity_id": DEVICE_1_ENTITY_ID},
+ "POWER",
)
- assert mocked_status.call_count == 3
- assert mocked_power_on.call_count == 1
-@patch("libsoundtouch.device.SoundTouchDevice.volume_up")
async def test_volume_up(
- mocked_volume_up, mocked_status, mocked_volume, hass, one_device
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device1_requests_mock_upnp,
+ device1_requests_mock_key,
):
"""Test volume up."""
- 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
-
- await hass.services.async_call(
- "media_player",
+ await setup_soundtouch(hass, device1_config)
+ await _test_key_service(
+ hass,
+ device1_requests_mock_key,
"volume_up",
- {"entity_id": "media_player.soundtouch_1"},
- True,
+ {"entity_id": DEVICE_1_ENTITY_ID},
+ "VOLUME_UP",
)
- assert mocked_volume.call_count == 3
- assert mocked_volume_up.call_count == 1
-@patch("libsoundtouch.device.SoundTouchDevice.volume_down")
async def test_volume_down(
- mocked_volume_down, mocked_status, mocked_volume, hass, one_device
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device1_requests_mock_upnp,
+ device1_requests_mock_key,
):
"""Test volume down."""
- 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
-
- await hass.services.async_call(
- "media_player",
+ await setup_soundtouch(hass, device1_config)
+ await _test_key_service(
+ hass,
+ device1_requests_mock_key,
"volume_down",
- {"entity_id": "media_player.soundtouch_1"},
- True,
+ {"entity_id": DEVICE_1_ENTITY_ID},
+ "VOLUME_DOWN",
)
- assert mocked_volume.call_count == 3
- assert mocked_volume_down.call_count == 1
-@patch("libsoundtouch.device.SoundTouchDevice.set_volume")
async def test_set_volume_level(
- mocked_set_volume, mocked_status, mocked_volume, hass, one_device
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device1_requests_mock_upnp,
+ device1_requests_mock_volume,
):
"""Test set volume level."""
- 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
+ await setup_soundtouch(hass, device1_config)
+ assert device1_requests_mock_volume.call_count == 0
await hass.services.async_call(
"media_player",
"volume_set",
- {"entity_id": "media_player.soundtouch_1", "volume_level": 0.17},
+ {"entity_id": DEVICE_1_ENTITY_ID, "volume_level": 0.17},
True,
)
- assert mocked_volume.call_count == 3
- mocked_set_volume.assert_called_with(17)
+ assert device1_requests_mock_volume.call_count == 1
+ assert "17" in device1_requests_mock_volume.last_request.text
-@patch("libsoundtouch.device.SoundTouchDevice.mute")
-async def test_mute(mocked_mute, mocked_status, mocked_volume, hass, one_device):
+async def test_mute(
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device1_requests_mock_upnp,
+ device1_requests_mock_key,
+):
"""Test mute volume."""
- 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
-
- await hass.services.async_call(
- "media_player",
+ await setup_soundtouch(hass, device1_config)
+ await _test_key_service(
+ hass,
+ device1_requests_mock_key,
"volume_mute",
- {"entity_id": "media_player.soundtouch_1", "is_volume_muted": True},
- True,
+ {"entity_id": DEVICE_1_ENTITY_ID, "is_volume_muted": True},
+ "MUTE",
)
- assert mocked_volume.call_count == 3
- assert mocked_mute.call_count == 1
-@patch("libsoundtouch.device.SoundTouchDevice.play")
-async def test_play(mocked_play, mocked_status, mocked_volume, hass, one_device):
+async def test_play(
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device1_requests_mock_upnp_paused,
+ device1_requests_mock_key,
+):
"""Test play command."""
- 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
-
- await hass.services.async_call(
- "media_player",
+ await setup_soundtouch(hass, device1_config)
+ await _test_key_service(
+ hass,
+ device1_requests_mock_key,
"media_play",
- {"entity_id": "media_player.soundtouch_1"},
- True,
+ {"entity_id": DEVICE_1_ENTITY_ID},
+ "PLAY",
)
- assert mocked_status.call_count == 3
- assert mocked_play.call_count == 1
-@patch("libsoundtouch.device.SoundTouchDevice.pause")
-async def test_pause(mocked_pause, mocked_status, mocked_volume, hass, one_device):
+async def test_pause(
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device1_requests_mock_upnp,
+ device1_requests_mock_key,
+):
"""Test pause command."""
- 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
-
- await hass.services.async_call(
- "media_player",
+ await setup_soundtouch(hass, device1_config)
+ await _test_key_service(
+ hass,
+ device1_requests_mock_key,
"media_pause",
- {"entity_id": "media_player.soundtouch_1"},
- True,
+ {"entity_id": DEVICE_1_ENTITY_ID},
+ "PAUSE",
)
- assert mocked_status.call_count == 3
- assert mocked_pause.call_count == 1
-@patch("libsoundtouch.device.SoundTouchDevice.play_pause")
async def test_play_pause(
- mocked_play_pause, mocked_status, mocked_volume, hass, one_device
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device1_requests_mock_upnp,
+ device1_requests_mock_key,
):
"""Test play/pause."""
- 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
-
- await hass.services.async_call(
- "media_player",
+ await setup_soundtouch(hass, device1_config)
+ await _test_key_service(
+ hass,
+ device1_requests_mock_key,
"media_play_pause",
- {"entity_id": "media_player.soundtouch_1"},
- True,
+ {"entity_id": DEVICE_1_ENTITY_ID},
+ "PLAY_PAUSE",
)
- assert mocked_status.call_count == 3
- assert mocked_play_pause.call_count == 1
-@patch("libsoundtouch.device.SoundTouchDevice.previous_track")
-@patch("libsoundtouch.device.SoundTouchDevice.next_track")
async def test_next_previous_track(
- mocked_next_track,
- mocked_previous_track,
- mocked_status,
- mocked_volume,
- hass,
- one_device,
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device1_requests_mock_upnp,
+ device1_requests_mock_key,
):
"""Test next/previous track."""
- 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
-
- await hass.services.async_call(
- "media_player",
+ await setup_soundtouch(hass, device1_config)
+ await _test_key_service(
+ hass,
+ device1_requests_mock_key,
"media_next_track",
- {"entity_id": "media_player.soundtouch_1"},
- True,
+ {"entity_id": DEVICE_1_ENTITY_ID},
+ "NEXT_TRACK",
)
- assert mocked_status.call_count == 3
- assert mocked_next_track.call_count == 1
- await hass.services.async_call(
- "media_player",
+ await _test_key_service(
+ hass,
+ device1_requests_mock_key,
"media_previous_track",
- {"entity_id": "media_player.soundtouch_1"},
- True,
+ {"entity_id": DEVICE_1_ENTITY_ID},
+ "PREV_TRACK",
)
- assert mocked_status.call_count == 4
- assert mocked_previous_track.call_count == 1
-@patch("libsoundtouch.device.SoundTouchDevice.select_preset")
-@patch("libsoundtouch.device.SoundTouchDevice.presets", side_effect=_mocked_presets)
async def test_play_media(
- mocked_presets, mocked_select_preset, mocked_status, mocked_volume, hass, one_device
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device1_requests_mock_standby,
+ device1_requests_mock_select,
):
"""Test play preset 1."""
- 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
+ await setup_soundtouch(hass, device1_config)
+ assert device1_requests_mock_select.call_count == 0
await hass.services.async_call(
"media_player",
"play_media",
{
- "entity_id": "media_player.soundtouch_1",
+ "entity_id": DEVICE_1_ENTITY_ID,
ATTR_MEDIA_CONTENT_TYPE: "PLAYLIST",
ATTR_MEDIA_CONTENT_ID: 1,
},
True,
)
- assert mocked_presets.call_count == 1
- assert mocked_select_preset.call_count == 1
+ assert device1_requests_mock_select.call_count == 1
+ assert (
+ 'location="http://homeassistant:8123/media/local/test.mp3"'
+ in device1_requests_mock_select.last_request.text
+ )
await hass.services.async_call(
"media_player",
"play_media",
{
- "entity_id": "media_player.soundtouch_1",
+ "entity_id": DEVICE_1_ENTITY_ID,
ATTR_MEDIA_CONTENT_TYPE: "PLAYLIST",
ATTR_MEDIA_CONTENT_ID: 2,
},
True,
)
- assert mocked_presets.call_count == 2
- assert mocked_select_preset.call_count == 1
+ assert device1_requests_mock_select.call_count == 2
+ assert "MockStation" in device1_requests_mock_select.last_request.text
-@patch("libsoundtouch.device.SoundTouchDevice.play_url")
async def test_play_media_url(
- mocked_play_url, mocked_status, mocked_volume, hass, one_device
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device1_requests_mock_standby,
+ device1_requests_mock_dlna,
):
"""Test play preset 1."""
- 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
+ await setup_soundtouch(hass, device1_config)
+ assert device1_requests_mock_dlna.call_count == 0
await hass.services.async_call(
"media_player",
"play_media",
{
- "entity_id": "media_player.soundtouch_1",
+ "entity_id": DEVICE_1_ENTITY_ID,
ATTR_MEDIA_CONTENT_TYPE: "MUSIC",
ATTR_MEDIA_CONTENT_ID: "http://fqdn/file.mp3",
},
True,
)
- mocked_play_url.assert_called_with("http://fqdn/file.mp3")
+ assert device1_requests_mock_dlna.call_count == 1
+ assert "http://fqdn/file.mp3" in device1_requests_mock_dlna.last_request.text
-@patch("libsoundtouch.device.SoundTouchDevice.select_source_aux")
async def test_select_source_aux(
- mocked_select_source_aux, mocked_status, mocked_volume, hass, one_device
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device1_requests_mock_standby,
+ device1_requests_mock_select,
):
"""Test select AUX."""
- await setup_soundtouch(hass, DEVICE_1_CONFIG)
+ await setup_soundtouch(hass, device1_config)
- assert mocked_select_source_aux.call_count == 0
+ assert device1_requests_mock_select.call_count == 0
await hass.services.async_call(
"media_player",
"select_source",
- {"entity_id": "media_player.soundtouch_1", ATTR_INPUT_SOURCE: "AUX"},
+ {"entity_id": DEVICE_1_ENTITY_ID, ATTR_INPUT_SOURCE: "AUX"},
True,
)
-
- assert mocked_select_source_aux.call_count == 1
+ assert device1_requests_mock_select.call_count == 1
+ assert "AUX" in device1_requests_mock_select.last_request.text
-@patch("libsoundtouch.device.SoundTouchDevice.select_source_bluetooth")
async def test_select_source_bluetooth(
- mocked_select_source_bluetooth, mocked_status, mocked_volume, hass, one_device
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device1_requests_mock_standby,
+ device1_requests_mock_select,
):
"""Test select Bluetooth."""
- await setup_soundtouch(hass, DEVICE_1_CONFIG)
+ await setup_soundtouch(hass, device1_config)
- assert mocked_select_source_bluetooth.call_count == 0
+ assert device1_requests_mock_select.call_count == 0
await hass.services.async_call(
"media_player",
"select_source",
- {"entity_id": "media_player.soundtouch_1", ATTR_INPUT_SOURCE: "BLUETOOTH"},
+ {"entity_id": DEVICE_1_ENTITY_ID, ATTR_INPUT_SOURCE: "BLUETOOTH"},
True,
)
-
- assert mocked_select_source_bluetooth.call_count == 1
+ assert device1_requests_mock_select.call_count == 1
+ assert "BLUETOOTH" in device1_requests_mock_select.last_request.text
-@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,
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device1_requests_mock_standby,
+ device1_requests_mock_select,
):
"""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 setup_soundtouch(hass, device1_config)
+ assert not device1_requests_mock_select.called
await hass.services.async_call(
"media_player",
"select_source",
{
- "entity_id": "media_player.soundtouch_1",
+ "entity_id": DEVICE_1_ENTITY_ID,
ATTR_INPUT_SOURCE: "SOMETHING_UNSUPPORTED",
},
True,
)
-
- assert mocked_select_source_aux.call_count == 0
- assert mocked_select_source_bluetooth.call_count == 0
+ assert not device1_requests_mock_select.called
-@patch("libsoundtouch.device.SoundTouchDevice.create_zone")
async def test_play_everywhere(
- mocked_create_zone, mocked_status, mocked_volume, hass, two_zones
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device2_config: dict[str, str],
+ device1_requests_mock_standby,
+ device2_requests_mock_standby,
+ device1_requests_mock_set_zone,
):
"""Test play everywhere."""
- mocked_device = two_zones
- await setup_soundtouch(hass, [DEVICE_1_CONFIG, DEVICE_2_CONFIG])
+ await setup_soundtouch(hass, device1_config, device2_config)
- assert mocked_device.call_count == 2
- assert mocked_status.call_count == 4
- assert mocked_volume.call_count == 4
-
- # one master, one slave => create zone
+ # one master, one slave => set zone
await hass.services.async_call(
- soundtouch.DOMAIN,
- soundtouch.SERVICE_PLAY_EVERYWHERE,
- {"master": "media_player.soundtouch_1"},
+ DOMAIN,
+ SERVICE_PLAY_EVERYWHERE,
+ {"master": DEVICE_1_ENTITY_ID},
True,
)
- assert mocked_create_zone.call_count == 1
+ assert device1_requests_mock_set_zone.call_count == 1
- # unknown master, create zone must not be called
+ # unknown master, set zone must not be called
await hass.services.async_call(
- soundtouch.DOMAIN,
- soundtouch.SERVICE_PLAY_EVERYWHERE,
+ DOMAIN,
+ SERVICE_PLAY_EVERYWHERE,
{"master": "media_player.entity_X"},
True,
)
- assert mocked_create_zone.call_count == 1
+ assert device1_requests_mock_set_zone.call_count == 1
- # no slaves, create zone must not be called
+ # remove second device
for entity in list(hass.data[DATA_SOUNDTOUCH]):
- if entity.entity_id == "media_player.soundtouch_1":
+ if entity.entity_id == DEVICE_1_ENTITY_ID:
continue
hass.data[DATA_SOUNDTOUCH].remove(entity)
await entity.async_remove()
+
+ # no slaves, set zone must not be called
await hass.services.async_call(
- soundtouch.DOMAIN,
- soundtouch.SERVICE_PLAY_EVERYWHERE,
- {"master": "media_player.soundtouch_1"},
+ DOMAIN,
+ SERVICE_PLAY_EVERYWHERE,
+ {"master": DEVICE_1_ENTITY_ID},
True,
)
- assert mocked_create_zone.call_count == 1
+ assert device1_requests_mock_set_zone.call_count == 1
-@patch("libsoundtouch.device.SoundTouchDevice.create_zone")
async def test_create_zone(
- mocked_create_zone, mocked_status, mocked_volume, hass, two_zones
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device2_config: dict[str, str],
+ device1_requests_mock_standby,
+ device2_requests_mock_standby,
+ device1_requests_mock_set_zone,
):
"""Test creating a zone."""
- mocked_device = two_zones
- await setup_soundtouch(hass, [DEVICE_1_CONFIG, DEVICE_2_CONFIG])
+ await setup_soundtouch(hass, device1_config, device2_config)
- assert mocked_device.call_count == 2
- assert mocked_status.call_count == 4
- assert mocked_volume.call_count == 4
+ assert device1_requests_mock_set_zone.call_count == 0
- # one master, one slave => create zone
+ # one master, one slave => set zone
await hass.services.async_call(
- soundtouch.DOMAIN,
- soundtouch.SERVICE_CREATE_ZONE,
+ DOMAIN,
+ SERVICE_CREATE_ZONE,
{
- "master": "media_player.soundtouch_1",
- "slaves": ["media_player.soundtouch_2"],
+ "master": DEVICE_1_ENTITY_ID,
+ "slaves": [DEVICE_2_ENTITY_ID],
},
True,
)
- assert mocked_create_zone.call_count == 1
+ assert device1_requests_mock_set_zone.call_count == 1
- # unknown master, create zone must not be called
+ # unknown master, set zone must not be called
await hass.services.async_call(
- soundtouch.DOMAIN,
- soundtouch.SERVICE_CREATE_ZONE,
- {"master": "media_player.entity_X", "slaves": ["media_player.soundtouch_2"]},
+ DOMAIN,
+ SERVICE_CREATE_ZONE,
+ {"master": "media_player.entity_X", "slaves": [DEVICE_2_ENTITY_ID]},
True,
)
- assert mocked_create_zone.call_count == 1
+ assert device1_requests_mock_set_zone.call_count == 1
- # no slaves, create zone must not be called
+ # no slaves, set zone must not be called
await hass.services.async_call(
- soundtouch.DOMAIN,
- soundtouch.SERVICE_CREATE_ZONE,
- {"master": "media_player.soundtouch_1", "slaves": []},
+ DOMAIN,
+ SERVICE_CREATE_ZONE,
+ {"master": DEVICE_1_ENTITY_ID, "slaves": []},
True,
)
- assert mocked_create_zone.call_count == 1
+ assert device1_requests_mock_set_zone.call_count == 1
-@patch("libsoundtouch.device.SoundTouchDevice.remove_zone_slave")
async def test_remove_zone_slave(
- mocked_remove_zone_slave, mocked_status, mocked_volume, hass, two_zones
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device2_config: dict[str, str],
+ device1_requests_mock_standby,
+ device2_requests_mock_standby,
+ device1_requests_mock_remove_zone_slave,
):
- """Test adding a slave to an existing zone."""
- mocked_device = two_zones
- await setup_soundtouch(hass, [DEVICE_1_CONFIG, DEVICE_2_CONFIG])
-
- assert mocked_device.call_count == 2
- assert mocked_status.call_count == 4
- assert mocked_volume.call_count == 4
+ """Test removing a slave from an existing zone."""
+ await setup_soundtouch(hass, device1_config, device2_config)
# remove one slave
await hass.services.async_call(
- soundtouch.DOMAIN,
- soundtouch.SERVICE_REMOVE_ZONE_SLAVE,
+ DOMAIN,
+ SERVICE_REMOVE_ZONE_SLAVE,
{
- "master": "media_player.soundtouch_1",
- "slaves": ["media_player.soundtouch_2"],
+ "master": DEVICE_1_ENTITY_ID,
+ "slaves": [DEVICE_2_ENTITY_ID],
},
True,
)
- assert mocked_remove_zone_slave.call_count == 1
+ assert device1_requests_mock_remove_zone_slave.call_count == 1
- # unknown master. add zone slave is not called
+ # unknown master, remove zone slave is not called
await hass.services.async_call(
- soundtouch.DOMAIN,
- soundtouch.SERVICE_REMOVE_ZONE_SLAVE,
- {"master": "media_player.entity_X", "slaves": ["media_player.soundtouch_2"]},
+ DOMAIN,
+ SERVICE_REMOVE_ZONE_SLAVE,
+ {"master": "media_player.entity_X", "slaves": [DEVICE_2_ENTITY_ID]},
True,
)
- assert mocked_remove_zone_slave.call_count == 1
+ assert device1_requests_mock_remove_zone_slave.call_count == 1
- # no slave to add, add zone slave is not called
+ # no slave to remove, remove zone slave is not called
await hass.services.async_call(
- soundtouch.DOMAIN,
- soundtouch.SERVICE_REMOVE_ZONE_SLAVE,
- {"master": "media_player.soundtouch_1", "slaves": []},
+ DOMAIN,
+ SERVICE_REMOVE_ZONE_SLAVE,
+ {"master": DEVICE_1_ENTITY_ID, "slaves": []},
True,
)
- assert mocked_remove_zone_slave.call_count == 1
+ assert device1_requests_mock_remove_zone_slave.call_count == 1
-@patch("libsoundtouch.device.SoundTouchDevice.add_zone_slave")
async def test_add_zone_slave(
- mocked_add_zone_slave,
- mocked_status,
- mocked_volume,
- hass,
- two_zones,
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device2_config: dict[str, str],
+ device1_requests_mock_standby,
+ device2_requests_mock_standby,
+ device1_requests_mock_add_zone_slave,
):
- """Test removing a slave from a zone."""
- mocked_device = two_zones
- await setup_soundtouch(hass, [DEVICE_1_CONFIG, DEVICE_2_CONFIG])
-
- assert mocked_device.call_count == 2
- assert mocked_status.call_count == 4
- assert mocked_volume.call_count == 4
+ """Test adding a slave to a zone."""
+ await setup_soundtouch(hass, device1_config, device2_config)
# add one slave
await hass.services.async_call(
- soundtouch.DOMAIN,
- soundtouch.SERVICE_ADD_ZONE_SLAVE,
+ DOMAIN,
+ SERVICE_ADD_ZONE_SLAVE,
{
- "master": "media_player.soundtouch_1",
- "slaves": ["media_player.soundtouch_2"],
+ "master": DEVICE_1_ENTITY_ID,
+ "slaves": [DEVICE_2_ENTITY_ID],
},
True,
)
- assert mocked_add_zone_slave.call_count == 1
+ assert device1_requests_mock_add_zone_slave.call_count == 1
# unknown master, add zone slave is not called
await hass.services.async_call(
- soundtouch.DOMAIN,
- soundtouch.SERVICE_ADD_ZONE_SLAVE,
- {"master": "media_player.entity_X", "slaves": ["media_player.soundtouch_2"]},
+ DOMAIN,
+ SERVICE_ADD_ZONE_SLAVE,
+ {"master": "media_player.entity_X", "slaves": [DEVICE_2_ENTITY_ID]},
True,
)
- assert mocked_add_zone_slave.call_count == 1
+ assert device1_requests_mock_add_zone_slave.call_count == 1
# no slave to add, add zone slave is not called
await hass.services.async_call(
- soundtouch.DOMAIN,
- soundtouch.SERVICE_ADD_ZONE_SLAVE,
- {"master": "media_player.soundtouch_1", "slaves": ["media_player.entity_X"]},
+ DOMAIN,
+ SERVICE_ADD_ZONE_SLAVE,
+ {"master": DEVICE_1_ENTITY_ID, "slaves": ["media_player.entity_X"]},
True,
)
- assert mocked_add_zone_slave.call_count == 1
+ assert device1_requests_mock_add_zone_slave.call_count == 1
-@patch("libsoundtouch.device.SoundTouchDevice.create_zone")
async def test_zone_attributes(
- mocked_create_zone,
- mocked_status,
- mocked_volume,
- hass,
- two_zones,
+ hass: HomeAssistant,
+ device1_config: dict[str, str],
+ device2_config: dict[str, str],
+ device1_requests_mock_standby,
+ device2_requests_mock_standby,
):
- """Test play everywhere."""
- mocked_device = two_zones
- await setup_soundtouch(hass, [DEVICE_1_CONFIG, DEVICE_2_CONFIG])
+ """Test zone attributes."""
+ await setup_soundtouch(hass, device1_config, device2_config)
- assert mocked_device.call_count == 2
- assert mocked_status.call_count == 4
- assert mocked_volume.call_count == 4
-
- entity_1_state = hass.states.get("media_player.soundtouch_1")
+ entity_1_state = hass.states.get(DEVICE_1_ENTITY_ID)
assert entity_1_state.attributes[ATTR_SOUNDTOUCH_ZONE]["is_master"]
assert (
- entity_1_state.attributes[ATTR_SOUNDTOUCH_ZONE]["master"]
- == "media_player.soundtouch_1"
+ entity_1_state.attributes[ATTR_SOUNDTOUCH_ZONE]["master"] == DEVICE_1_ENTITY_ID
)
assert entity_1_state.attributes[ATTR_SOUNDTOUCH_ZONE]["slaves"] == [
- "media_player.soundtouch_2"
+ DEVICE_2_ENTITY_ID
]
assert entity_1_state.attributes[ATTR_SOUNDTOUCH_GROUP] == [
- "media_player.soundtouch_1",
- "media_player.soundtouch_2",
+ DEVICE_1_ENTITY_ID,
+ DEVICE_2_ENTITY_ID,
]
- entity_2_state = hass.states.get("media_player.soundtouch_2")
+
+ entity_2_state = hass.states.get(DEVICE_2_ENTITY_ID)
assert not entity_2_state.attributes[ATTR_SOUNDTOUCH_ZONE]["is_master"]
assert (
- entity_2_state.attributes[ATTR_SOUNDTOUCH_ZONE]["master"]
- == "media_player.soundtouch_1"
+ entity_2_state.attributes[ATTR_SOUNDTOUCH_ZONE]["master"] == DEVICE_1_ENTITY_ID
)
assert entity_2_state.attributes[ATTR_SOUNDTOUCH_ZONE]["slaves"] == [
- "media_player.soundtouch_2"
+ DEVICE_2_ENTITY_ID
]
assert entity_2_state.attributes[ATTR_SOUNDTOUCH_GROUP] == [
- "media_player.soundtouch_1",
- "media_player.soundtouch_2",
+ DEVICE_1_ENTITY_ID,
+ DEVICE_2_ENTITY_ID,
]