Remove support for deprecated Sonos configuration (#23385)
parent
d038d2426b
commit
5dbf58d67f
|
@ -1,10 +1,26 @@
|
|||
"""Support to embed Sonos."""
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.helpers import config_entry_flow
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.media_player import DOMAIN as MP_DOMAIN
|
||||
from homeassistant.const import CONF_HOSTS
|
||||
from homeassistant.helpers import config_entry_flow, config_validation as cv
|
||||
|
||||
DOMAIN = 'sonos'
|
||||
|
||||
CONF_ADVERTISE_ADDR = 'advertise_addr'
|
||||
CONF_INTERFACE_ADDR = 'interface_addr'
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
DOMAIN: vol.Schema({
|
||||
MP_DOMAIN: vol.Schema({
|
||||
vol.Optional(CONF_ADVERTISE_ADDR): cv.string,
|
||||
vol.Optional(CONF_INTERFACE_ADDR): cv.string,
|
||||
vol.Optional(CONF_HOSTS): vol.All(cv.ensure_list_csv, [cv.string]),
|
||||
}),
|
||||
}),
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
|
||||
async def async_setup(hass, config):
|
||||
"""Set up the Sonos component."""
|
||||
|
@ -22,7 +38,7 @@ async def async_setup(hass, config):
|
|||
async def async_setup_entry(hass, entry):
|
||||
"""Set up Sonos from a config entry."""
|
||||
hass.async_create_task(hass.config_entries.async_forward_entry_setup(
|
||||
entry, 'media_player'))
|
||||
entry, MP_DOMAIN))
|
||||
return True
|
||||
|
||||
|
||||
|
|
|
@ -10,20 +10,21 @@ import async_timeout
|
|||
import requests
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.media_player import (
|
||||
PLATFORM_SCHEMA, MediaPlayerDevice)
|
||||
from homeassistant.components.media_player import MediaPlayerDevice
|
||||
from homeassistant.components.media_player.const import (
|
||||
ATTR_MEDIA_ENQUEUE, DOMAIN, MEDIA_TYPE_MUSIC, SUPPORT_CLEAR_PLAYLIST,
|
||||
SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_PLAY_MEDIA,
|
||||
SUPPORT_PREVIOUS_TRACK, SUPPORT_SEEK, SUPPORT_SELECT_SOURCE,
|
||||
SUPPORT_SHUFFLE_SET, SUPPORT_STOP, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET)
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID, ATTR_TIME, CONF_HOSTS, STATE_IDLE, STATE_OFF, STATE_PAUSED,
|
||||
ATTR_ENTITY_ID, ATTR_TIME, STATE_IDLE, STATE_OFF, STATE_PAUSED,
|
||||
STATE_PLAYING)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
from . import DOMAIN as SONOS_DOMAIN
|
||||
from . import (
|
||||
CONF_ADVERTISE_ADDR, CONF_HOSTS, CONF_INTERFACE_ADDR,
|
||||
DOMAIN as SONOS_DOMAIN)
|
||||
|
||||
DEPENDENCIES = ('sonos',)
|
||||
|
||||
|
@ -54,9 +55,6 @@ DATA_SONOS = 'sonos_media_player'
|
|||
SOURCE_LINEIN = 'Line-in'
|
||||
SOURCE_TV = 'TV'
|
||||
|
||||
CONF_ADVERTISE_ADDR = 'advertise_addr'
|
||||
CONF_INTERFACE_ADDR = 'interface_addr'
|
||||
|
||||
# Service call validation schemas
|
||||
ATTR_SLEEP_TIME = 'sleep_time'
|
||||
ATTR_ALARM_ID = 'alarm_id'
|
||||
|
@ -72,12 +70,6 @@ ATTR_SONOS_GROUP = 'sonos_group'
|
|||
|
||||
UPNP_ERRORS_TO_IGNORE = ['701', '711', '712']
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_ADVERTISE_ADDR): cv.string,
|
||||
vol.Optional(CONF_INTERFACE_ADDR): cv.string,
|
||||
vol.Optional(CONF_HOSTS): vol.All(cv.ensure_list, [cv.string]),
|
||||
})
|
||||
|
||||
SONOS_SCHEMA = vol.Schema({
|
||||
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
||||
})
|
||||
|
@ -119,57 +111,34 @@ class SonosData:
|
|||
self.topology_condition = asyncio.Condition(loop=hass.loop)
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
"""Set up the Sonos platform.
|
||||
|
||||
Deprecated.
|
||||
"""
|
||||
_LOGGER.warning('Loading Sonos via platform config is deprecated.')
|
||||
_setup_platform(hass, config, add_entities, discovery_info)
|
||||
async def async_setup_platform(hass,
|
||||
config,
|
||||
async_add_entities,
|
||||
discovery_info=None):
|
||||
"""Set up the Sonos platform. Obsolete."""
|
||||
_LOGGER.error(
|
||||
'Loading Sonos by media_player platform config is no longer supported')
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up Sonos from a config entry."""
|
||||
def add_entities(entities, update_before_add=False):
|
||||
"""Sync version of async add entities."""
|
||||
hass.add_job(async_add_entities, entities, update_before_add)
|
||||
|
||||
hass.async_add_executor_job(
|
||||
_setup_platform, hass, hass.data[SONOS_DOMAIN].get('media_player', {}),
|
||||
add_entities, None)
|
||||
|
||||
|
||||
def _setup_platform(hass, config, add_entities, discovery_info):
|
||||
"""Set up the Sonos platform."""
|
||||
import pysonos
|
||||
|
||||
if DATA_SONOS not in hass.data:
|
||||
hass.data[DATA_SONOS] = SonosData(hass)
|
||||
|
||||
config = hass.data[SONOS_DOMAIN].get('media_player', {})
|
||||
|
||||
advertise_addr = config.get(CONF_ADVERTISE_ADDR)
|
||||
if advertise_addr:
|
||||
pysonos.config.EVENT_ADVERTISE_IP = advertise_addr
|
||||
|
||||
players = []
|
||||
if discovery_info:
|
||||
player = pysonos.SoCo(discovery_info.get('host'))
|
||||
|
||||
# If host already exists by config
|
||||
if player.uid in hass.data[DATA_SONOS].uids:
|
||||
return
|
||||
|
||||
# If invisible, such as a stereo slave
|
||||
if not player.is_visible:
|
||||
return
|
||||
|
||||
players.append(player)
|
||||
else:
|
||||
def _create_sonos_entities():
|
||||
"""Discover players and return a list of SonosEntity objects."""
|
||||
players = []
|
||||
hosts = config.get(CONF_HOSTS)
|
||||
|
||||
if hosts:
|
||||
# Support retro compatibility with comma separated list of hosts
|
||||
# from config
|
||||
hosts = hosts[0] if len(hosts) == 1 else hosts
|
||||
hosts = hosts.split(',') if isinstance(hosts, str) else hosts
|
||||
for host in hosts:
|
||||
try:
|
||||
players.append(pysonos.SoCo(socket.gethostbyname(host)))
|
||||
|
@ -182,11 +151,14 @@ def _setup_platform(hass, config, add_entities, discovery_info):
|
|||
|
||||
if not players:
|
||||
_LOGGER.warning("No Sonos speakers found")
|
||||
return
|
||||
|
||||
hass.data[DATA_SONOS].uids.update(p.uid for p in players)
|
||||
add_entities(SonosEntity(p) for p in players)
|
||||
_LOGGER.debug("Added %s Sonos speakers", len(players))
|
||||
return [SonosEntity(p) for p in players]
|
||||
|
||||
entities = await hass.async_add_executor_job(_create_sonos_entities)
|
||||
hass.data[DATA_SONOS].uids.update(e.unique_id for e in entities)
|
||||
|
||||
async_add_entities(entities)
|
||||
_LOGGER.debug("Added %s Sonos speakers", len(entities))
|
||||
|
||||
def _service_to_entities(service):
|
||||
"""Extract and return entities from service call."""
|
||||
|
@ -216,19 +188,19 @@ def _setup_platform(hass, config, add_entities, discovery_info):
|
|||
await SonosEntity.restore_multi(
|
||||
hass, entities, service.data[ATTR_WITH_GROUP])
|
||||
|
||||
hass.services.register(
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_JOIN, async_service_handle,
|
||||
schema=SONOS_JOIN_SCHEMA)
|
||||
|
||||
hass.services.register(
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_UNJOIN, async_service_handle,
|
||||
schema=SONOS_SCHEMA)
|
||||
|
||||
hass.services.register(
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SNAPSHOT, async_service_handle,
|
||||
schema=SONOS_STATES_SCHEMA)
|
||||
|
||||
hass.services.register(
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_RESTORE, async_service_handle,
|
||||
schema=SONOS_STATES_SCHEMA)
|
||||
|
||||
|
@ -244,19 +216,19 @@ def _setup_platform(hass, config, add_entities, discovery_info):
|
|||
elif service.service == SERVICE_SET_OPTION:
|
||||
entity.set_option(**service.data)
|
||||
|
||||
hass.services.register(
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SET_TIMER, service_handle,
|
||||
schema=SONOS_SET_TIMER_SCHEMA)
|
||||
|
||||
hass.services.register(
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_CLEAR_TIMER, service_handle,
|
||||
schema=SONOS_SCHEMA)
|
||||
|
||||
hass.services.register(
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_UPDATE_ALARM, service_handle,
|
||||
schema=SONOS_UPDATE_ALARM_SCHEMA)
|
||||
|
||||
hass.services.register(
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SET_OPTION, service_handle,
|
||||
schema=SONOS_SET_OPTION_SCHEMA)
|
||||
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
"""Configuration for Sonos tests."""
|
||||
from asynctest.mock import Mock, patch as patch
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.sonos import DOMAIN
|
||||
from homeassistant.components.media_player import DOMAIN as MP_DOMAIN
|
||||
from homeassistant.const import CONF_HOSTS
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.fixture(name="config_entry")
|
||||
def config_entry_fixture():
|
||||
"""Create a mock Sonos config entry."""
|
||||
return MockConfigEntry(domain=DOMAIN, title='Sonos')
|
||||
|
||||
|
||||
@pytest.fixture(name="soco")
|
||||
def soco_fixture(music_library, speaker_info, dummy_soco_service):
|
||||
"""Create a mock pysonos SoCo fixture."""
|
||||
with patch('pysonos.SoCo', autospec=True) as mock, \
|
||||
patch('socket.gethostbyname', return_value='192.168.42.2'):
|
||||
mock_soco = mock.return_value
|
||||
mock_soco.uid = 'RINCON_test'
|
||||
mock_soco.music_library = music_library
|
||||
mock_soco.get_speaker_info.return_value = speaker_info
|
||||
mock_soco.avTransport = dummy_soco_service
|
||||
mock_soco.renderingControl = dummy_soco_service
|
||||
mock_soco.zoneGroupTopology = dummy_soco_service
|
||||
mock_soco.contentDirectory = dummy_soco_service
|
||||
|
||||
yield mock_soco
|
||||
|
||||
|
||||
@pytest.fixture(name="discover")
|
||||
def discover_fixture(soco):
|
||||
"""Create a mock pysonos discover fixture."""
|
||||
with patch('pysonos.discover') as mock:
|
||||
mock.return_value = {soco}
|
||||
yield mock
|
||||
|
||||
|
||||
@pytest.fixture(name="config")
|
||||
def config_fixture():
|
||||
"""Create hass config fixture."""
|
||||
return {
|
||||
DOMAIN: {
|
||||
MP_DOMAIN: {
|
||||
CONF_HOSTS: ['192.168.42.1']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(name="dummy_soco_service")
|
||||
def dummy_soco_service_fixture():
|
||||
"""Create dummy_soco_service fixture."""
|
||||
service = Mock()
|
||||
service.subscribe = Mock()
|
||||
return service
|
||||
|
||||
|
||||
@pytest.fixture(name="music_library")
|
||||
def music_library_fixture():
|
||||
"""Create music_library fixture."""
|
||||
music_library = Mock()
|
||||
music_library.get_sonos_favorites.return_value = []
|
||||
return music_library
|
||||
|
||||
|
||||
@pytest.fixture(name="speaker_info")
|
||||
def speaker_info_fixture():
|
||||
"""Create speaker_info fixture."""
|
||||
return {
|
||||
'zone_name': 'Zone A',
|
||||
'model_name': 'Model Name',
|
||||
}
|
|
@ -35,7 +35,9 @@ async def test_configuring_sonos_creates_entry(hass):
|
|||
patch('pysonos.discover', return_value=True):
|
||||
await async_setup_component(hass, sonos.DOMAIN, {
|
||||
'sonos': {
|
||||
'some_config': 'to_trigger_import'
|
||||
'media_player': {
|
||||
'interface_addr': '127.0.0.1',
|
||||
}
|
||||
}
|
||||
})
|
||||
await hass.async_block_till_done()
|
||||
|
|
|
@ -1,360 +1,22 @@
|
|||
"""The tests for the Demo Media player platform."""
|
||||
import datetime
|
||||
import socket
|
||||
import unittest
|
||||
import pysonos.snapshot
|
||||
from unittest import mock
|
||||
import pysonos
|
||||
from pysonos import alarms
|
||||
|
||||
from homeassistant.setup import setup_component
|
||||
from homeassistant.components.sonos import media_player as sonos
|
||||
from homeassistant.components.media_player.const import DOMAIN
|
||||
from homeassistant.components.sonos.media_player import CONF_INTERFACE_ADDR
|
||||
from homeassistant.const import CONF_HOSTS, CONF_PLATFORM
|
||||
from homeassistant.util.async_ import run_coroutine_threadsafe
|
||||
|
||||
from tests.common import get_test_home_assistant
|
||||
|
||||
ENTITY_ID = 'media_player.kitchen'
|
||||
"""Tests for the Sonos Media Player platform."""
|
||||
from homeassistant.components.sonos import media_player, DOMAIN
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
|
||||
class pysonosDiscoverMock():
|
||||
"""Mock class for the pysonos.discover method."""
|
||||
|
||||
def discover(interface_addr, all_households=False):
|
||||
"""Return tuple of pysonos.SoCo objects representing found speakers."""
|
||||
return {SoCoMock('192.0.2.1')}
|
||||
async def setup_platform(hass, config_entry, config):
|
||||
"""Set up the media player platform for testing."""
|
||||
config_entry.add_to_hass(hass)
|
||||
assert await async_setup_component(hass, DOMAIN, config)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
class AvTransportMock():
|
||||
"""Mock class for the avTransport property on pysonos.SoCo object."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize ethe Transport mock."""
|
||||
pass
|
||||
|
||||
def GetMediaInfo(self, _):
|
||||
"""Get the media details."""
|
||||
return {
|
||||
'CurrentURI': '',
|
||||
'CurrentURIMetaData': ''
|
||||
}
|
||||
async def test_async_setup_entry_hosts(hass, config_entry, config, soco):
|
||||
"""Test static setup."""
|
||||
await setup_platform(hass, config_entry, config)
|
||||
assert hass.data[media_player.DATA_SONOS].entities[0].soco == soco
|
||||
|
||||
|
||||
class MusicLibraryMock():
|
||||
"""Mock class for the music_library property on pysonos.SoCo object."""
|
||||
|
||||
def get_sonos_favorites(self):
|
||||
"""Return favorites."""
|
||||
return []
|
||||
|
||||
|
||||
class CacheMock():
|
||||
"""Mock class for the _zgs_cache property on pysonos.SoCo object."""
|
||||
|
||||
def clear(self):
|
||||
"""Clear cache."""
|
||||
pass
|
||||
|
||||
|
||||
class SoCoMock():
|
||||
"""Mock class for the pysonos.SoCo object."""
|
||||
|
||||
def __init__(self, ip):
|
||||
"""Initialize SoCo object."""
|
||||
self.ip_address = ip
|
||||
self.is_visible = True
|
||||
self.volume = 50
|
||||
self.mute = False
|
||||
self.shuffle = False
|
||||
self.night_mode = False
|
||||
self.dialog_mode = False
|
||||
self.music_library = MusicLibraryMock()
|
||||
self.avTransport = AvTransportMock()
|
||||
self._zgs_cache = CacheMock()
|
||||
|
||||
def get_sonos_favorites(self):
|
||||
"""Get favorites list from sonos."""
|
||||
return {'favorites': []}
|
||||
|
||||
def get_speaker_info(self, force):
|
||||
"""Return a dict with various data points about the speaker."""
|
||||
return {'serial_number': 'B8-E9-37-BO-OC-BA:2',
|
||||
'software_version': '32.11-30071',
|
||||
'uid': 'RINCON_B8E937BOOCBA02500',
|
||||
'zone_icon': 'x-rincon-roomicon:kitchen',
|
||||
'mac_address': 'B8:E9:37:BO:OC:BA',
|
||||
'zone_name': 'Kitchen',
|
||||
'model_name': 'Sonos PLAY:1',
|
||||
'hardware_version': '1.8.1.2-1'}
|
||||
|
||||
def get_current_transport_info(self):
|
||||
"""Return a dict with the current state of the speaker."""
|
||||
return {'current_transport_speed': '1',
|
||||
'current_transport_state': 'STOPPED',
|
||||
'current_transport_status': 'OK'}
|
||||
|
||||
def get_current_track_info(self):
|
||||
"""Return a dict with the current track information."""
|
||||
return {'album': '',
|
||||
'uri': '',
|
||||
'title': '',
|
||||
'artist': '',
|
||||
'duration': '0:00:00',
|
||||
'album_art': '',
|
||||
'position': '0:00:00',
|
||||
'playlist_position': '0',
|
||||
'metadata': ''}
|
||||
|
||||
def is_coordinator(self):
|
||||
"""Return true if coordinator."""
|
||||
return True
|
||||
|
||||
def join(self, master):
|
||||
"""Join speaker to a group."""
|
||||
return
|
||||
|
||||
def set_sleep_timer(self, sleep_time_seconds):
|
||||
"""Set the sleep timer."""
|
||||
return
|
||||
|
||||
def unjoin(self):
|
||||
"""Cause the speaker to separate itself from other speakers."""
|
||||
return
|
||||
|
||||
def uid(self):
|
||||
"""Return a player uid."""
|
||||
return "RINCON_XXXXXXXXXXXXXXXXX"
|
||||
|
||||
def group(self):
|
||||
"""Return all group data of this player."""
|
||||
return
|
||||
|
||||
|
||||
def add_entities_factory(hass):
|
||||
"""Add entities factory."""
|
||||
def add_entities(entities, update_befor_add=False):
|
||||
"""Fake add entity."""
|
||||
hass.data[sonos.DATA_SONOS].entities = list(entities)
|
||||
|
||||
return add_entities
|
||||
|
||||
|
||||
class TestSonosMediaPlayer(unittest.TestCase):
|
||||
"""Test the media_player module."""
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
def setUp(self):
|
||||
"""Set up things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
|
||||
def monkey_available(self):
|
||||
"""Make a monkey available."""
|
||||
return True
|
||||
|
||||
# Monkey patches
|
||||
self.real_available = sonos.SonosEntity.available
|
||||
sonos.SonosEntity.available = monkey_available
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
def tearDown(self):
|
||||
"""Stop everything that was started."""
|
||||
# Monkey patches
|
||||
sonos.SonosEntity.available = self.real_available
|
||||
self.hass.stop()
|
||||
|
||||
@mock.patch('pysonos.SoCo', new=SoCoMock)
|
||||
@mock.patch('socket.create_connection', side_effect=socket.error())
|
||||
def test_ensure_setup_discovery(self, *args):
|
||||
"""Test a single device using the autodiscovery provided by HASS."""
|
||||
sonos.setup_platform(self.hass, {}, add_entities_factory(self.hass), {
|
||||
'host': '192.0.2.1'
|
||||
})
|
||||
|
||||
entities = self.hass.data[sonos.DATA_SONOS].entities
|
||||
assert len(entities) == 1
|
||||
assert entities[0].name == 'Kitchen'
|
||||
|
||||
@mock.patch('pysonos.SoCo', new=SoCoMock)
|
||||
@mock.patch('socket.create_connection', side_effect=socket.error())
|
||||
@mock.patch('pysonos.discover')
|
||||
def test_ensure_setup_config_interface_addr(self, discover_mock, *args):
|
||||
"""Test an interface address config'd by the HASS config file."""
|
||||
discover_mock.return_value = {SoCoMock('192.0.2.1')}
|
||||
|
||||
config = {
|
||||
DOMAIN: {
|
||||
CONF_PLATFORM: 'sonos',
|
||||
CONF_INTERFACE_ADDR: '192.0.1.1',
|
||||
}
|
||||
}
|
||||
|
||||
assert setup_component(self.hass, DOMAIN, config)
|
||||
|
||||
assert len(self.hass.data[sonos.DATA_SONOS].entities) == 1
|
||||
assert discover_mock.call_count == 1
|
||||
|
||||
@mock.patch('pysonos.SoCo', new=SoCoMock)
|
||||
@mock.patch('socket.create_connection', side_effect=socket.error())
|
||||
def test_ensure_setup_config_hosts_string_single(self, *args):
|
||||
"""Test a single address config'd by the HASS config file."""
|
||||
config = {
|
||||
DOMAIN: {
|
||||
CONF_PLATFORM: 'sonos',
|
||||
CONF_HOSTS: ['192.0.2.1'],
|
||||
}
|
||||
}
|
||||
|
||||
assert setup_component(self.hass, DOMAIN, config)
|
||||
|
||||
entities = self.hass.data[sonos.DATA_SONOS].entities
|
||||
assert len(entities) == 1
|
||||
assert entities[0].name == 'Kitchen'
|
||||
|
||||
@mock.patch('pysonos.SoCo', new=SoCoMock)
|
||||
@mock.patch('socket.create_connection', side_effect=socket.error())
|
||||
def test_ensure_setup_config_hosts_string_multiple(self, *args):
|
||||
"""Test multiple address string config'd by the HASS config file."""
|
||||
config = {
|
||||
DOMAIN: {
|
||||
CONF_PLATFORM: 'sonos',
|
||||
CONF_HOSTS: ['192.0.2.1,192.168.2.2'],
|
||||
}
|
||||
}
|
||||
|
||||
assert setup_component(self.hass, DOMAIN, config)
|
||||
|
||||
entities = self.hass.data[sonos.DATA_SONOS].entities
|
||||
assert len(entities) == 2
|
||||
assert entities[0].name == 'Kitchen'
|
||||
|
||||
@mock.patch('pysonos.SoCo', new=SoCoMock)
|
||||
@mock.patch('socket.create_connection', side_effect=socket.error())
|
||||
def test_ensure_setup_config_hosts_list(self, *args):
|
||||
"""Test a multiple address list config'd by the HASS config file."""
|
||||
config = {
|
||||
DOMAIN: {
|
||||
CONF_PLATFORM: 'sonos',
|
||||
CONF_HOSTS: ['192.0.2.1', '192.168.2.2'],
|
||||
}
|
||||
}
|
||||
|
||||
assert setup_component(self.hass, DOMAIN, config)
|
||||
|
||||
entities = self.hass.data[sonos.DATA_SONOS].entities
|
||||
assert len(entities) == 2
|
||||
assert entities[0].name == 'Kitchen'
|
||||
|
||||
@mock.patch('pysonos.SoCo', new=SoCoMock)
|
||||
@mock.patch.object(pysonos, 'discover', new=pysonosDiscoverMock.discover)
|
||||
@mock.patch('socket.create_connection', side_effect=socket.error())
|
||||
def test_ensure_setup_sonos_discovery(self, *args):
|
||||
"""Test a single device using the autodiscovery provided by Sonos."""
|
||||
sonos.setup_platform(self.hass, {}, add_entities_factory(self.hass))
|
||||
entities = self.hass.data[sonos.DATA_SONOS].entities
|
||||
assert len(entities) == 1
|
||||
assert entities[0].name == 'Kitchen'
|
||||
|
||||
@mock.patch('pysonos.SoCo', new=SoCoMock)
|
||||
@mock.patch('socket.create_connection', side_effect=socket.error())
|
||||
@mock.patch.object(SoCoMock, 'set_sleep_timer')
|
||||
def test_sonos_set_sleep_timer(self, set_sleep_timerMock, *args):
|
||||
"""Ensure pysonos methods called for sonos_set_sleep_timer service."""
|
||||
sonos.setup_platform(self.hass, {}, add_entities_factory(self.hass), {
|
||||
'host': '192.0.2.1'
|
||||
})
|
||||
entity = self.hass.data[sonos.DATA_SONOS].entities[-1]
|
||||
entity.hass = self.hass
|
||||
|
||||
entity.set_sleep_timer(30)
|
||||
set_sleep_timerMock.assert_called_once_with(30)
|
||||
|
||||
@mock.patch('pysonos.SoCo', new=SoCoMock)
|
||||
@mock.patch('socket.create_connection', side_effect=socket.error())
|
||||
@mock.patch.object(SoCoMock, 'set_sleep_timer')
|
||||
def test_sonos_clear_sleep_timer(self, set_sleep_timerMock, *args):
|
||||
"""Ensure pysonos method called for sonos_clear_sleep_timer service."""
|
||||
sonos.setup_platform(self.hass, {}, add_entities_factory(self.hass), {
|
||||
'host': '192.0.2.1'
|
||||
})
|
||||
entity = self.hass.data[sonos.DATA_SONOS].entities[-1]
|
||||
entity.hass = self.hass
|
||||
|
||||
entity.set_sleep_timer(None)
|
||||
set_sleep_timerMock.assert_called_once_with(None)
|
||||
|
||||
@mock.patch('pysonos.SoCo', new=SoCoMock)
|
||||
@mock.patch('pysonos.alarms.Alarm')
|
||||
@mock.patch('socket.create_connection', side_effect=socket.error())
|
||||
def test_set_alarm(self, pysonos_mock, alarm_mock, *args):
|
||||
"""Ensure pysonos methods called for sonos_set_sleep_timer service."""
|
||||
sonos.setup_platform(self.hass, {}, add_entities_factory(self.hass), {
|
||||
'host': '192.0.2.1'
|
||||
})
|
||||
entity = self.hass.data[sonos.DATA_SONOS].entities[-1]
|
||||
entity.hass = self.hass
|
||||
alarm1 = alarms.Alarm(pysonos_mock)
|
||||
alarm1.configure_mock(_alarm_id="1", start_time=None, enabled=False,
|
||||
include_linked_zones=False, volume=100)
|
||||
with mock.patch('pysonos.alarms.get_alarms', return_value=[alarm1]):
|
||||
attrs = {
|
||||
'time': datetime.time(12, 00),
|
||||
'enabled': True,
|
||||
'include_linked_zones': True,
|
||||
'volume': 0.30,
|
||||
}
|
||||
entity.set_alarm(alarm_id=2)
|
||||
alarm1.save.assert_not_called()
|
||||
entity.set_alarm(alarm_id=1, **attrs)
|
||||
assert alarm1.enabled == attrs['enabled']
|
||||
assert alarm1.start_time == attrs['time']
|
||||
assert alarm1.include_linked_zones == \
|
||||
attrs['include_linked_zones']
|
||||
assert alarm1.volume == 30
|
||||
alarm1.save.assert_called_once_with()
|
||||
|
||||
@mock.patch('pysonos.SoCo', new=SoCoMock)
|
||||
@mock.patch('socket.create_connection', side_effect=socket.error())
|
||||
@mock.patch.object(pysonos.snapshot.Snapshot, 'snapshot')
|
||||
def test_sonos_snapshot(self, snapshotMock, *args):
|
||||
"""Ensure pysonos methods called for sonos_snapshot service."""
|
||||
sonos.setup_platform(self.hass, {}, add_entities_factory(self.hass), {
|
||||
'host': '192.0.2.1'
|
||||
})
|
||||
entities = self.hass.data[sonos.DATA_SONOS].entities
|
||||
entity = entities[-1]
|
||||
entity.hass = self.hass
|
||||
|
||||
snapshotMock.return_value = True
|
||||
entity.soco.group = mock.MagicMock()
|
||||
entity.soco.group.members = [e.soco for e in entities]
|
||||
run_coroutine_threadsafe(
|
||||
sonos.SonosEntity.snapshot_multi(self.hass, entities, True),
|
||||
self.hass.loop).result()
|
||||
assert snapshotMock.call_count == 1
|
||||
assert snapshotMock.call_args == mock.call()
|
||||
|
||||
@mock.patch('pysonos.SoCo', new=SoCoMock)
|
||||
@mock.patch('socket.create_connection', side_effect=socket.error())
|
||||
@mock.patch.object(pysonos.snapshot.Snapshot, 'restore')
|
||||
def test_sonos_restore(self, restoreMock, *args):
|
||||
"""Ensure pysonos methods called for sonos_restore service."""
|
||||
from pysonos.snapshot import Snapshot
|
||||
|
||||
sonos.setup_platform(self.hass, {}, add_entities_factory(self.hass), {
|
||||
'host': '192.0.2.1'
|
||||
})
|
||||
entities = self.hass.data[sonos.DATA_SONOS].entities
|
||||
entity = entities[-1]
|
||||
entity.hass = self.hass
|
||||
|
||||
restoreMock.return_value = True
|
||||
entity._snapshot_group = mock.MagicMock()
|
||||
entity._snapshot_group.members = [e.soco for e in entities]
|
||||
entity._soco_snapshot = Snapshot(entity.soco)
|
||||
run_coroutine_threadsafe(
|
||||
sonos.SonosEntity.restore_multi(self.hass, entities, True),
|
||||
self.hass.loop).result()
|
||||
assert restoreMock.call_count == 1
|
||||
assert restoreMock.call_args == mock.call()
|
||||
async def test_async_setup_entry_discover(hass, config_entry, discover):
|
||||
"""Test discovery setup."""
|
||||
await setup_platform(hass, config_entry, {})
|
||||
assert hass.data[media_player.DATA_SONOS].uids == {'RINCON_test'}
|
||||
|
|
Loading…
Reference in New Issue