diff --git a/.coveragerc b/.coveragerc
index 91cb5ef640493f64288dd352eec71b05a55e2896..fac7edfa42b816f5c3ee8a2552b207fa35f15619 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -360,6 +360,7 @@ omit =
     homeassistant/components/sensor/miflora.py
     homeassistant/components/sensor/modem_callerid.py
     homeassistant/components/sensor/mqtt_room.py
+    homeassistant/components/sensor/mvglive.py
     homeassistant/components/sensor/netdata.py
     homeassistant/components/sensor/neurio_energy.py
     homeassistant/components/sensor/nut.py
diff --git a/homeassistant/components/sensor/mvglive.py b/homeassistant/components/sensor/mvglive.py
new file mode 100644
index 0000000000000000000000000000000000000000..734744378bac7aac7816fcd609d57eb507ce5606
--- /dev/null
+++ b/homeassistant/components/sensor/mvglive.py
@@ -0,0 +1,159 @@
+"""
+Support for real-time departure information for public transport in Munich.
+
+For more details about this platform, please refer to the documentation at
+https://home-assistant.io/components/sensor.mvglive/
+"""
+
+import logging
+from datetime import timedelta
+
+import voluptuous as vol
+
+from homeassistant.util import Throttle
+from homeassistant.helpers.entity import Entity
+from homeassistant.components.sensor import PLATFORM_SCHEMA
+from homeassistant.const import STATE_UNKNOWN
+import homeassistant.helpers.config_validation as cv
+
+_LOGGER = logging.getLogger(__name__)
+# A typo in the file name of the PyPI version prevents installation from PyPI
+REQUIREMENTS = ["PyMVGLive==1.1.3"]
+ICON = 'mdi:bus'
+
+# Return cached results if last scan was less then this time ago.
+MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=15)
+
+CONF_STATION = 'station'
+CONF_DEST = 'destination'
+CONF_LINE = 'line'
+CONF_OFFSET = 'offset'
+CONF_UBAHN = 'ubahn'
+CONF_TRAM = 'tram'
+CONF_BUS = 'bus'
+CONF_SBAHN = 'sbahn'
+
+PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
+    vol.Required(CONF_STATION): cv.string,
+    vol.Optional(CONF_DEST, default=None): cv.string,
+    vol.Optional(CONF_LINE, default=None): cv.string,
+    vol.Optional(CONF_OFFSET, default=0): cv.positive_int,
+    vol.Optional(CONF_UBAHN, default=True): cv.boolean,
+    vol.Optional(CONF_TRAM, default=True): cv.boolean,
+    vol.Optional(CONF_BUS, default=True): cv.boolean,
+    vol.Optional(CONF_SBAHN, default=True): cv.boolean,
+})
+
+
+def setup_platform(hass, config, add_devices, discovery_info=None):
+    """Setup the MVG Live Sensor."""
+    station = config.get(CONF_STATION)
+    destination = config.get(CONF_DEST)
+    line = config.get(CONF_LINE)
+    offset = config.get(CONF_OFFSET)
+    ubahn = config.get(CONF_UBAHN)
+    tram = config.get(CONF_TRAM)
+    bus = config.get(CONF_BUS)
+    sbahn = config.get(CONF_SBAHN)
+
+    add_devices([MVGLiveSensor(station, destination, line,
+                               offset, ubahn, tram, bus, sbahn)], True)
+
+
+# pylint: disable=too-few-public-methods
+class MVGLiveSensor(Entity):
+    """Implementation of an MVG Live sensor."""
+
+    def __init__(self, station, destination, line,
+                 offset, ubahn, tram, bus, sbahn):
+        """Initialize the sensor."""
+        self._station = station
+        self._destination = destination
+        self._line = line
+        self.data = MVGLiveData(station, destination, line,
+                                offset, ubahn, tram, bus, sbahn)
+        self._state = STATE_UNKNOWN
+
+    @property
+    def name(self):
+        """Return the name of the sensor."""
+        # e.g.
+        # 'Hauptbahnhof (S1)'
+        # 'Hauptbahnhof-Marienplatz'
+        # 'Hauptbahnhof-Marienplatz (S1)'
+        namestr = self._station
+        if self._destination:
+            namestr = namestr + '-' + self._destination
+        if self._line:
+            namestr = namestr + ' (' + self._line + ')'
+        return namestr
+
+    @property
+    def icon(self):
+        """Return the icon for the frontend."""
+        return ICON
+
+    @property
+    def state(self):
+        """Return the departure time of the next train."""
+        return self._state
+
+    @property
+    def state_attributes(self):
+        """Return the state attributes."""
+        return self.data.nextdeparture
+
+    def update(self):
+        """Get the latest data and update the state."""
+        self.data.update()
+        if not self.data.nextdeparture:
+            self._state = '-'
+        else:
+            self._state = self.data.nextdeparture.get('time', '-')
+
+
+class MVGLiveData(object):
+    """Pull data from the mvg-live.de web page."""
+
+    def __init__(self, station, destination, line,
+                 offset, ubahn, tram, bus, sbahn):
+        """Initialize the sensor."""
+        import MVGLive
+        self._station = station
+        self._destination = destination
+        self._line = line
+        self._offset = offset
+        self._ubahn = ubahn
+        self._tram = tram
+        self._bus = bus
+        self._sbahn = sbahn
+        self.mvg = MVGLive.MVGLive()
+        self.nextdeparture = {}
+
+    @Throttle(MIN_TIME_BETWEEN_UPDATES)
+    def update(self):
+        """Update the connection data."""
+        try:
+            _departures = self.mvg.getlivedata(station=self._station,
+                                               ubahn=self._ubahn,
+                                               tram=self._tram,
+                                               bus=self._bus,
+                                               sbahn=self._sbahn)
+        except ValueError:
+            self.nextdeparture = {}
+            _LOGGER.warning("Returned data not understood.")
+            return
+        for _departure in _departures:
+            # find the first departure meeting the criteria
+            if not _departure['destination'].startswith(self._destination):
+                continue
+            elif _departure['time'] < self._offset:
+                continue
+            # now select the relevant data
+            _nextdep = {}
+            for k in ['destination', 'linename', 'time', 'direction',
+                      'product']:
+                _nextdep[k] = _departure.get(k, '')
+            _nextdep['time'] = int(_nextdep['time'])
+            self.nextdeparture = _nextdep
+            break
diff --git a/requirements_all.txt b/requirements_all.txt
index 05655e3ed07bf56bf10ad51c5cb1e7a49dcbca63..475993accedfb9d54178c16c2c629586df7acd65 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -21,6 +21,9 @@ PyISY==1.0.7
 # homeassistant.components.notify.html5
 PyJWT==1.4.2
 
+# homeassistant.components.sensor.mvglive
+PyMVGLive==1.1.3
+
 # homeassistant.components.arduino
 PyMata==2.13