Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 19 additions & 19 deletions homeassistant/components/media_player/sonos.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
Support to interface with Sonos players (via SoCo).
Support to interface with Sonos players.

For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/media_player.sonos/
Expand Down Expand Up @@ -31,11 +31,11 @@

_LOGGER = logging.getLogger(__name__)

# Quiet down soco logging to just actual problems.
logging.getLogger('soco').setLevel(logging.WARNING)
logging.getLogger('soco.events').setLevel(logging.ERROR)
logging.getLogger('soco.data_structures_entry').setLevel(logging.ERROR)
_SOCO_SERVICES_LOGGER = logging.getLogger('soco.services')
# Quiet down pysonos logging to just actual problems.
logging.getLogger('pysonos').setLevel(logging.WARNING)
logging.getLogger('pysonos.events').setLevel(logging.ERROR)
logging.getLogger('pysonos.data_structures_entry').setLevel(logging.ERROR)
_SOCO_SERVICES_LOGGER = logging.getLogger('pysonos.services')

SUPPORT_SONOS = SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE |\
SUPPORT_PLAY | SUPPORT_PAUSE | SUPPORT_STOP | SUPPORT_SELECT_SOURCE |\
Expand Down Expand Up @@ -143,18 +143,18 @@ def add_entities(devices, update_before_add=False):

def _setup_platform(hass, config, add_entities, discovery_info):
"""Set up the Sonos platform."""
import soco
import pysonos

if DATA_SONOS not in hass.data:
hass.data[DATA_SONOS] = SonosData()

advertise_addr = config.get(CONF_ADVERTISE_ADDR)
if advertise_addr:
soco.config.EVENT_ADVERTISE_IP = advertise_addr
pysonos.config.EVENT_ADVERTISE_IP = advertise_addr

players = []
if discovery_info:
player = soco.SoCo(discovery_info.get('host'))
player = pysonos.SoCo(discovery_info.get('host'))

# If device already exists by config
if player.uid in hass.data[DATA_SONOS].uids:
Expand All @@ -174,11 +174,11 @@ def _setup_platform(hass, config, add_entities, discovery_info):
hosts = hosts.split(',') if isinstance(hosts, str) else hosts
for host in hosts:
try:
players.append(soco.SoCo(socket.gethostbyname(host)))
players.append(pysonos.SoCo(socket.gethostbyname(host)))
except OSError:
_LOGGER.warning("Failed to initialize '%s'", host)
else:
players = soco.discover(
players = pysonos.discover(
interface_addr=config.get(CONF_INTERFACE_ADDR))

if not players:
Expand Down Expand Up @@ -287,7 +287,7 @@ def decorator(funct):
@ft.wraps(funct)
def wrapper(*args, **kwargs):
"""Wrap for all soco UPnP exception."""
from soco.exceptions import SoCoUPnPException, SoCoException
from pysonos.exceptions import SoCoUPnPException, SoCoException

# Temporarily disable SoCo logging because it will log the
# UPnP exception otherwise
Expand Down Expand Up @@ -612,9 +612,9 @@ def update_media_radio(self, variables, track_info):
current_uri_metadata = media_info["CurrentURIMetaData"]
if current_uri_metadata not in ('', 'NOT_IMPLEMENTED', None):
# currently soco does not have an API for this
import soco
current_uri_metadata = soco.xml.XML.fromstring(
soco.utils.really_utf8(current_uri_metadata))
import pysonos
current_uri_metadata = pysonos.xml.XML.fromstring(
pysonos.utils.really_utf8(current_uri_metadata))

md_title = current_uri_metadata.findtext(
'.//{http://purl.org/dc/elements/1.1/}title')
Expand Down Expand Up @@ -950,7 +950,7 @@ def play_media(self, media_type, media_id, **kwargs):
If ATTR_MEDIA_ENQUEUE is True, add `media_id` to the queue.
"""
if kwargs.get(ATTR_MEDIA_ENQUEUE):
from soco.exceptions import SoCoUPnPException
from pysonos.exceptions import SoCoUPnPException
try:
self.soco.add_uri_to_queue(media_id)
except SoCoUPnPException:
Expand Down Expand Up @@ -981,7 +981,7 @@ def unjoin(self):
@soco_error()
def snapshot(self, with_group=True):
"""Snapshot the player."""
from soco.snapshot import Snapshot
from pysonos.snapshot import Snapshot

self._soco_snapshot = Snapshot(self.soco)
self._soco_snapshot.snapshot()
Expand All @@ -996,7 +996,7 @@ def snapshot(self, with_group=True):
@soco_error()
def restore(self, with_group=True):
"""Restore snapshot for the player."""
from soco.exceptions import SoCoException
from pysonos.exceptions import SoCoException
try:
# need catch exception if a coordinator is going to slave.
# this state will recover with group part.
Expand Down Expand Up @@ -1060,7 +1060,7 @@ def clear_sleep_timer(self):
@soco_coordinator
def set_alarm(self, **data):
"""Set the alarm clock on the player."""
from soco import alarms
from pysonos import alarms
alarm = None
for one_alarm in alarms.get_alarms(self.soco):
# pylint: disable=protected-access
Expand Down
6 changes: 3 additions & 3 deletions homeassistant/components/sonos/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


DOMAIN = 'sonos'
REQUIREMENTS = ['SoCo==0.16']
REQUIREMENTS = ['pysonos==0.0.1']


async def async_setup(hass, config):
Expand All @@ -29,9 +29,9 @@ async def async_setup_entry(hass, entry):

async def _async_has_devices(hass):
"""Return if there are devices that can be discovered."""
import soco
import pysonos

return await hass.async_add_executor_job(soco.discover)
return await hass.async_add_executor_job(pysonos.discover)


config_entry_flow.register_discovery_flow(
Expand Down
6 changes: 3 additions & 3 deletions requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,6 @@ PyXiaomiGateway==0.10.0
# homeassistant.components.remember_the_milk
RtmAPI==0.7.0

# homeassistant.components.sonos
SoCo==0.16

# homeassistant.components.sensor.travisci
TravisPy==0.3.5

Expand Down Expand Up @@ -1057,6 +1054,9 @@ pysma==0.2
# homeassistant.components.switch.snmp
pysnmp==4.4.5

# homeassistant.components.sonos
pysonos==0.0.1

# homeassistant.components.notify.stride
pystride==0.1.7

Expand Down
6 changes: 3 additions & 3 deletions requirements_test_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ HAP-python==2.2.2
# homeassistant.components.sensor.rmvtransport
PyRMVtransport==0.1

# homeassistant.components.sonos
SoCo==0.16

# homeassistant.components.device_tracker.automatic
aioautomatic==0.6.5

Expand Down Expand Up @@ -167,6 +164,9 @@ pyotp==2.2.6
# homeassistant.components.qwikswitch
pyqwikswitch==0.8

# homeassistant.components.sonos
pysonos==0.0.1

# homeassistant.components.sensor.darksky
# homeassistant.components.weather.darksky
python-forecastio==1.4.0
Expand Down
2 changes: 1 addition & 1 deletion script/gen_requirements_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
'pynx584',
'pyopenuv',
'pyotp',
'pysonos',
'pyqwikswitch',
'PyRMVtransport',
'python-forecastio',
Expand All @@ -92,7 +93,6 @@
'ring_doorbell',
'rxv',
'sleepyq',
'SoCo',
'somecomfort',
'sqlalchemy',
'statsd',
Expand Down
70 changes: 35 additions & 35 deletions tests/components/media_player/test_sonos.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
import datetime
import socket
import unittest
import soco.snapshot
import pysonos.snapshot
from unittest import mock
import soco
from soco import alarms
import pysonos
from pysonos import alarms

from homeassistant.setup import setup_component
from homeassistant.components.media_player import sonos, DOMAIN
Expand All @@ -17,16 +17,16 @@
ENTITY_ID = 'media_player.kitchen'


class socoDiscoverMock():
"""Mock class for the soco.discover method."""
class pysonosDiscoverMock():
"""Mock class for the pysonos.discover method."""

def discover(interface_addr):
"""Return tuple of soco.SoCo objects representing found speakers."""
"""Return tuple of pysonos.SoCo objects representing found speakers."""
return {SoCoMock('192.0.2.1')}


class AvTransportMock():
"""Mock class for the avTransport property on soco.SoCo object."""
"""Mock class for the avTransport property on pysonos.SoCo object."""

def __init__(self):
"""Initialize ethe Transport mock."""
Expand All @@ -41,18 +41,18 @@ def GetMediaInfo(self, _):


class MusicLibraryMock():
"""Mock class for the music_library property on soco.SoCo object."""
"""Mock class for the music_library property on pysonos.SoCo object."""

def get_sonos_favorites(self):
"""Return favorites."""
return []


class SoCoMock():
"""Mock class for the soco.SoCo object."""
"""Mock class for the pysonos.SoCo object."""

def __init__(self, ip):
"""Initialize soco object."""
"""Initialize SoCo object."""
self.ip_address = ip
self.is_visible = True
self.volume = 50
Expand Down Expand Up @@ -153,7 +153,7 @@ def tearDown(self):
sonos.SonosDevice.available = self.real_available
self.hass.stop()

@mock.patch('soco.SoCo', new=SoCoMock)
@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."""
Expand All @@ -165,9 +165,9 @@ def test_ensure_setup_discovery(self, *args):
self.assertEqual(len(devices), 1)
self.assertEqual(devices[0].name, 'Kitchen')

@mock.patch('soco.SoCo', new=SoCoMock)
@mock.patch('pysonos.SoCo', new=SoCoMock)
@mock.patch('socket.create_connection', side_effect=socket.error())
@mock.patch('soco.discover')
@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')}
Expand All @@ -184,7 +184,7 @@ def test_ensure_setup_config_interface_addr(self, discover_mock, *args):
self.assertEqual(len(self.hass.data[sonos.DATA_SONOS].devices), 1)
self.assertEqual(discover_mock.call_count, 1)

@mock.patch('soco.SoCo', new=SoCoMock)
@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."""
Expand All @@ -201,7 +201,7 @@ def test_ensure_setup_config_hosts_string_single(self, *args):
self.assertEqual(len(devices), 1)
self.assertEqual(devices[0].name, 'Kitchen')

@mock.patch('soco.SoCo', new=SoCoMock)
@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."""
Expand All @@ -218,7 +218,7 @@ def test_ensure_setup_config_hosts_string_multiple(self, *args):
self.assertEqual(len(devices), 2)
self.assertEqual(devices[0].name, 'Kitchen')

@mock.patch('soco.SoCo', new=SoCoMock)
@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."""
Expand All @@ -235,8 +235,8 @@ def test_ensure_setup_config_hosts_list(self, *args):
self.assertEqual(len(devices), 2)
self.assertEqual(devices[0].name, 'Kitchen')

@mock.patch('soco.SoCo', new=SoCoMock)
@mock.patch.object(soco, 'discover', new=socoDiscoverMock.discover)
@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."""
Expand All @@ -245,11 +245,11 @@ def test_ensure_setup_sonos_discovery(self, *args):
self.assertEqual(len(devices), 1)
self.assertEqual(devices[0].name, 'Kitchen')

@mock.patch('soco.SoCo', new=SoCoMock)
@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):
"""Ensuring soco methods called for sonos_set_sleep_timer service."""
"""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'
})
Expand All @@ -259,11 +259,11 @@ def test_sonos_set_sleep_timer(self, set_sleep_timerMock, *args):
device.set_sleep_timer(30)
set_sleep_timerMock.assert_called_once_with(30)

@mock.patch('soco.SoCo', new=SoCoMock)
@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):
"""Ensuring soco methods called for sonos_clear_sleep_timer service."""
"""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'
})
Expand All @@ -273,20 +273,20 @@ def test_sonos_clear_sleep_timer(self, set_sleep_timerMock, *args):
device.set_sleep_timer(None)
set_sleep_timerMock.assert_called_once_with(None)

@mock.patch('soco.SoCo', new=SoCoMock)
@mock.patch('soco.alarms.Alarm')
@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, soco_mock, alarm_mock, *args):
"""Ensuring soco methods called for sonos_set_sleep_timer service."""
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'
})
device = list(self.hass.data[sonos.DATA_SONOS].devices)[-1]
device.hass = self.hass
alarm1 = alarms.Alarm(soco_mock)
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('soco.alarms.get_alarms', return_value=[alarm1]):
with mock.patch('pysonos.alarms.get_alarms', return_value=[alarm1]):
attrs = {
'time': datetime.time(12, 00),
'enabled': True,
Expand All @@ -303,11 +303,11 @@ def test_set_alarm(self, soco_mock, alarm_mock, *args):
self.assertEqual(alarm1.volume, 30)
alarm1.save.assert_called_once_with()

@mock.patch('soco.SoCo', new=SoCoMock)
@mock.patch('pysonos.SoCo', new=SoCoMock)
@mock.patch('socket.create_connection', side_effect=socket.error())
@mock.patch.object(soco.snapshot.Snapshot, 'snapshot')
@mock.patch.object(pysonos.snapshot.Snapshot, 'snapshot')
def test_sonos_snapshot(self, snapshotMock, *args):
"""Ensuring soco methods called for sonos_snapshot service."""
"""Ensure pysonos methods called for sonos_snapshot service."""
sonos.setup_platform(self.hass, {}, add_entities_factory(self.hass), {
'host': '192.0.2.1'
})
Expand All @@ -319,12 +319,12 @@ def test_sonos_snapshot(self, snapshotMock, *args):
self.assertEqual(snapshotMock.call_count, 1)
self.assertEqual(snapshotMock.call_args, mock.call())

@mock.patch('soco.SoCo', new=SoCoMock)
@mock.patch('pysonos.SoCo', new=SoCoMock)
@mock.patch('socket.create_connection', side_effect=socket.error())
@mock.patch.object(soco.snapshot.Snapshot, 'restore')
@mock.patch.object(pysonos.snapshot.Snapshot, 'restore')
def test_sonos_restore(self, restoreMock, *args):
"""Ensuring soco methods called for sonos_restor service."""
from soco.snapshot import Snapshot
"""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'
Expand Down
Loading