From 880f18a37ef0f076f8645d2d56d9644642177e46 Mon Sep 17 00:00:00 2001
From: Diogo Gomes <diogogomes@gmail.com>
Date: Sat, 3 Feb 2018 13:29:55 +0000
Subject: [PATCH] Mediaroom (#11864)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* make port mapping optional

* dependencies + improvements

* Added bytes and packets sensors from IGD

* flake8 check

* new sensor with upnp counters

* checks

* whitespaces in blank line

* requirements update

* added sensor.upnp to .coveragerc

* downgrade miniupnpc

Latest version of miniupnpc is 2.0, but pypi only has 1.9

Fortunately it is enough

* revert to non async

miniupnpc will do network calls, so this component can’t be moved to
coroutine

* hof hof

forgot to remove import ot asyncio

* Add baudrate option

* merge

* Added Mediaroom media_player component

* Updated header

Works with MEO and VDF set-top boxes in Portugal

* formatting

* Development Checklist (done)

* fix formatting according to houndci-bot

* more format fixing (tks houndci-bot)

* more fixes

* too much cleanup...

* too much

* pylint check

* Initial commit

Basic configuration testing

* flake8 and lint
---
 .coveragerc                                   |   1 +
 .../components/media_player/mediaroom.py      | 199 ++++++++++++++++++
 requirements_all.txt                          |   3 +
 .../components/media_player/test_mediaroom.py |  32 +++
 4 files changed, 235 insertions(+)
 create mode 100644 homeassistant/components/media_player/mediaroom.py
 create mode 100644 tests/components/media_player/test_mediaroom.py

diff --git a/.coveragerc b/.coveragerc
index 7287dcb143f..bef4f1cf1ee 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -439,6 +439,7 @@ omit =
     homeassistant/components/media_player/kodi.py
     homeassistant/components/media_player/lg_netcast.py
     homeassistant/components/media_player/liveboxplaytv.py
+    homeassistant/components/media_player/mediaroom.py
     homeassistant/components/media_player/mpchc.py
     homeassistant/components/media_player/mpd.py
     homeassistant/components/media_player/nad.py
diff --git a/homeassistant/components/media_player/mediaroom.py b/homeassistant/components/media_player/mediaroom.py
new file mode 100644
index 00000000000..549ad931e35
--- /dev/null
+++ b/homeassistant/components/media_player/mediaroom.py
@@ -0,0 +1,199 @@
+"""
+Support for the Mediaroom Set-up-box.
+
+For more details about this platform, please refer to the documentation at
+https://home-assistant.io/components/media_player.mediaroom/
+"""
+import logging
+import voluptuous as vol
+
+from homeassistant.components.media_player import (
+    MEDIA_TYPE_CHANNEL, SUPPORT_PAUSE, SUPPORT_PLAY_MEDIA,
+    SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_STOP, PLATFORM_SCHEMA,
+    SUPPORT_NEXT_TRACK, SUPPORT_PREVIOUS_TRACK, SUPPORT_PLAY,
+    SUPPORT_VOLUME_STEP, SUPPORT_VOLUME_MUTE,
+    MediaPlayerDevice)
+from homeassistant.const import (
+    CONF_HOST, CONF_NAME, CONF_OPTIMISTIC, CONF_TIMEOUT,
+    STATE_PAUSED, STATE_PLAYING, STATE_STANDBY,
+    STATE_ON)
+import homeassistant.helpers.config_validation as cv
+REQUIREMENTS = ['pymediaroom==0.5']
+
+_LOGGER = logging.getLogger(__name__)
+
+NOTIFICATION_TITLE = 'Mediaroom Media Player Setup'
+NOTIFICATION_ID = 'mediaroom_notification'
+DEFAULT_NAME = 'Mediaroom STB'
+DEFAULT_TIMEOUT = 9
+
+KNOWN_HOSTS = []
+
+SUPPORT_MEDIAROOM = SUPPORT_PAUSE | SUPPORT_TURN_ON | SUPPORT_TURN_OFF | \
+    SUPPORT_VOLUME_STEP | SUPPORT_VOLUME_MUTE | \
+    SUPPORT_PLAY_MEDIA | SUPPORT_STOP | SUPPORT_NEXT_TRACK | \
+    SUPPORT_PREVIOUS_TRACK | SUPPORT_PLAY
+
+PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
+    vol.Optional(CONF_HOST): cv.string,
+    vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
+    vol.Optional(CONF_OPTIMISTIC, default=False): cv.boolean,
+    vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
+})
+
+
+def setup_platform(hass, config, add_devices, discovery_info=None):
+    """Set up the Mediaroom platform."""
+    hosts = []
+
+    host = config.get(CONF_HOST, None)
+    if host is None:
+        _LOGGER.info("Trying to discover Mediaroom STB")
+
+        from pymediaroom import Remote
+
+        host = Remote.discover(KNOWN_HOSTS)
+        if host is None:
+            # Can't find any STB
+            return False
+    hosts.append(host)
+    KNOWN_HOSTS.append(host)
+
+    stbs = []
+
+    try:
+        for host in hosts:
+            stbs.append(MediaroomDevice(
+                config.get(CONF_NAME),
+                host,
+                config.get(CONF_OPTIMISTIC),
+                config.get(CONF_TIMEOUT)
+            ))
+
+    except ConnectionRefusedError:
+        hass.components.persistent_notification.create(
+            'Error: Unable to initialize mediaroom at {}<br />'
+            'Check its network connection or consider '
+            'using auto discovery.<br />'
+            'You will need to restart hass after fixing.'
+            ''.format(host),
+            title=NOTIFICATION_TITLE,
+            notification_id=NOTIFICATION_ID)
+
+    add_devices(stbs)
+
+    return True
+
+
+class MediaroomDevice(MediaPlayerDevice):
+    """Representation of a Mediaroom set-up-box on the network."""
+
+    def __init__(self, name, host, optimistic=False, timeout=DEFAULT_TIMEOUT):
+        """Initialize the device."""
+        from pymediaroom import Remote
+
+        self.stb = Remote(host, timeout=timeout)
+        _LOGGER.info(
+            "Found %s at %s%s", name, host,
+            " - I'm optimistic" if optimistic else "")
+        self._name = name
+        self._is_standby = not optimistic
+        self._current = None
+        self._optimistic = optimistic
+        self._state = STATE_STANDBY
+
+    def update(self):
+        """Retrieve latest state."""
+        if not self._optimistic:
+            self._is_standby = self.stb.get_standby()
+        if self._is_standby:
+            self._state = STATE_STANDBY
+        elif self._state not in [STATE_PLAYING, STATE_PAUSED]:
+            self._state = STATE_PLAYING
+        _LOGGER.debug(
+            "%s(%s) is [%s]",
+            self._name, self.stb.stb_ip, self._state)
+
+    def play_media(self, media_type, media_id, **kwargs):
+        """Play media."""
+        _LOGGER.debug(
+            "%s(%s) Play media: %s (%s)",
+            self._name, self.stb.stb_ip, media_id, media_type)
+        if media_type != MEDIA_TYPE_CHANNEL:
+            _LOGGER.error('invalid media type')
+            return
+        if media_id.isdigit():
+            media_id = int(media_id)
+        else:
+            return
+        self.stb.send_cmd(media_id)
+        self._state = STATE_PLAYING
+
+    @property
+    def name(self):
+        """Return the name of the device."""
+        return self._name
+
+    # MediaPlayerDevice properties and methods
+    @property
+    def state(self):
+        """Return the state of the device."""
+        return self._state
+
+    @property
+    def supported_features(self):
+        """Flag media player features that are supported."""
+        return SUPPORT_MEDIAROOM
+
+    @property
+    def media_content_type(self):
+        """Return the content type of current playing media."""
+        return MEDIA_TYPE_CHANNEL
+
+    def turn_on(self):
+        """Turn on the receiver."""
+        self.stb.send_cmd('Power')
+        self._state = STATE_ON
+
+    def turn_off(self):
+        """Turn off the receiver."""
+        self.stb.send_cmd('Power')
+        self._state = STATE_STANDBY
+
+    def media_play(self):
+        """Send play command."""
+        _LOGGER.debug("media_play()")
+        self.stb.send_cmd('PlayPause')
+        self._state = STATE_PLAYING
+
+    def media_pause(self):
+        """Send pause command."""
+        self.stb.send_cmd('PlayPause')
+        self._state = STATE_PAUSED
+
+    def media_stop(self):
+        """Send stop command."""
+        self.stb.send_cmd('Stop')
+        self._state = STATE_PAUSED
+
+    def media_previous_track(self):
+        """Send Program Down command."""
+        self.stb.send_cmd('ProgDown')
+        self._state = STATE_PLAYING
+
+    def media_next_track(self):
+        """Send Program Up command."""
+        self.stb.send_cmd('ProgUp')
+        self._state = STATE_PLAYING
+
+    def volume_up(self):
+        """Send volume up command."""
+        self.stb.send_cmd('VolUp')
+
+    def volume_down(self):
+        """Send volume up command."""
+        self.stb.send_cmd('VolDown')
+
+    def mute_volume(self, mute):
+        """Send mute command."""
+        self.stb.send_cmd('Mute')
diff --git a/requirements_all.txt b/requirements_all.txt
index 3bf11f9e27c..9750d3700ce 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -789,6 +789,9 @@ pylutron==0.1.0
 # homeassistant.components.notify.mailgun
 pymailgunner==1.4
 
+# homeassistant.components.media_player.mediaroom
+pymediaroom==0.5
+
 # homeassistant.components.mochad
 pymochad==0.2.0
 
diff --git a/tests/components/media_player/test_mediaroom.py b/tests/components/media_player/test_mediaroom.py
new file mode 100644
index 00000000000..7c7922b87be
--- /dev/null
+++ b/tests/components/media_player/test_mediaroom.py
@@ -0,0 +1,32 @@
+"""The tests for the mediaroom media_player."""
+
+import unittest
+
+from homeassistant.setup import setup_component
+import homeassistant.components.media_player as media_player
+from tests.common import (
+    assert_setup_component, get_test_home_assistant)
+
+
+class TestMediaroom(unittest.TestCase):
+    """Tests the Mediaroom Component."""
+
+    def setUp(self):
+        """Initialize values for this test case class."""
+        self.hass = get_test_home_assistant()
+
+    def tearDown(self):  # pylint: disable=invalid-name
+        """Stop everything that we started."""
+        self.hass.stop()
+
+    def test_mediaroom_config(self):
+        """Test set up the platform with basic configuration."""
+        config = {
+            media_player.DOMAIN: {
+                'platform': 'mediaroom',
+                'name': 'Living Room'
+            }
+        }
+        with assert_setup_component(1, media_player.DOMAIN) as result_config:
+            assert setup_component(self.hass, media_player.DOMAIN, config)
+        assert result_config[media_player.DOMAIN]
-- 
GitLab