diff --git a/.coveragerc b/.coveragerc
index 26b6413b4c0d1b7c1d604b1529b4e81145ec1f93..fe4796ae53e0051597d70b3227cd97bd55eef76b 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -66,6 +66,7 @@ omit =
     homeassistant/components/*/scsgate.py
 
     homeassistant/components/upnp.py
+    homeassistant/components/zeroconf.py
 
     homeassistant/components/binary_sensor/arest.py
     homeassistant/components/binary_sensor/rest.py
@@ -175,6 +176,7 @@ omit =
     homeassistant/components/thermostat/homematic.py
     homeassistant/components/thermostat/proliphix.py
     homeassistant/components/thermostat/radiotherm.py
+    homeassistant/components/*/thinkingcleaner.py
 
 
 [report]
diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py
index 1866e972e280002b01377308f87363452747f168..beb9e4a7214e70c63b3c583f9121e4acd102081d 100644
--- a/homeassistant/components/device_tracker/__init__.py
+++ b/homeassistant/components/device_tracker/__init__.py
@@ -94,7 +94,7 @@ def setup(hass, config):
     yaml_path = hass.config.path(YAML_DEVICES)
 
     conf = config.get(DOMAIN, {})
-    if isinstance(conf, list):
+    if isinstance(conf, list) and len(conf) > 0:
         conf = conf[0]
     consider_home = timedelta(
         seconds=util.convert(conf.get(CONF_CONSIDER_HOME), int,
diff --git a/homeassistant/components/discovery.py b/homeassistant/components/discovery.py
index f03fdf35929dd7b25965e8d2b9c7e375dceba207..900d826e61a643bfeaf8bc7ade0ce0f5da7dcccf 100644
--- a/homeassistant/components/discovery.py
+++ b/homeassistant/components/discovery.py
@@ -15,7 +15,7 @@ from homeassistant.const import (
     EVENT_PLATFORM_DISCOVERED)
 
 DOMAIN = "discovery"
-REQUIREMENTS = ['netdisco==0.6.2']
+REQUIREMENTS = ['netdisco==0.6.4']
 
 SCAN_INTERVAL = 300  # seconds
 
diff --git a/homeassistant/components/sensor/thinkingcleaner.py b/homeassistant/components/sensor/thinkingcleaner.py
new file mode 100644
index 0000000000000000000000000000000000000000..1ba8593650ea2d47809bc1c24ecf19686b8ba1be
--- /dev/null
+++ b/homeassistant/components/sensor/thinkingcleaner.py
@@ -0,0 +1,114 @@
+"""Support for ThinkingCleaner."""
+import logging
+from datetime import timedelta
+
+import homeassistant.util as util
+from homeassistant.helpers.entity import Entity
+
+_LOGGER = logging.getLogger(__name__)
+
+REQUIREMENTS = ['https://github.com/TheRealLink/pythinkingcleaner'
+                '/archive/v0.0.2.zip'
+                '#pythinkingcleaner==0.0.2']
+
+MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
+MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(milliseconds=100)
+
+SENSOR_TYPES = {
+    'battery': ['Battery', '%', 'mdi:battery'],
+    'state': ['State', None, None],
+    'capacity': ['Capacity', None, None],
+}
+
+STATES = {
+    'st_base': 'On homebase: Not Charging',
+    'st_base_recon': 'On homebase: Reconditioning Charging',
+    'st_base_full': 'On homebase: Full Charging',
+    'st_base_trickle': 'On homebase: Trickle Charging',
+    'st_base_wait': 'On homebase: Waiting',
+    'st_plug': 'Plugged in: Not Charging',
+    'st_plug_recon': 'Plugged in: Reconditioning Charging',
+    'st_plug_full': 'Plugged in: Full Charging',
+    'st_plug_trickle': 'Plugged in: Trickle Charging',
+    'st_plug_wait': 'Plugged in: Waiting',
+    'st_stopped': 'Stopped',
+    'st_clean': 'Cleaning',
+    'st_cleanstop': 'Stopped with cleaning',
+    'st_clean_spot': 'Spot cleaning',
+    'st_clean_max': 'Max cleaning',
+    'st_delayed': 'Delayed cleaning will start soon',
+    'st_dock': 'Searching Homebase',
+    'st_pickup': 'Roomba picked up',
+    'st_remote': 'Remote control driving',
+    'st_wait': 'Waiting for command',
+    'st_off': 'Off',
+    'st_error': 'Error',
+    'st_locate': 'Find me!',
+    'st_unknown': 'Unknown state',
+}
+
+
+def setup_platform(hass, config, add_devices, discovery_info=None):
+    """Setup the ThinkingCleaner platform."""
+    from pythinkingcleaner import Discovery
+
+    discovery = Discovery()
+    devices = discovery.discover()
+
+    @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
+    def update_devices():
+        """Update all devices."""
+        for device_object in devices:
+            device_object.update()
+
+    dev = []
+    for device in devices:
+        for type_name in SENSOR_TYPES.keys():
+            dev.append(ThinkingCleanerSensor(device, type_name,
+                                             update_devices))
+
+    add_devices(dev)
+
+
+class ThinkingCleanerSensor(Entity):
+    """ThinkingCleaner Sensor."""
+
+    def __init__(self, tc_object, sensor_type, update_devices):
+        """Initialize the ThinkingCleaner."""
+        self.type = sensor_type
+
+        self._tc_object = tc_object
+        self._update_devices = update_devices
+        self._unit_of_measurement = SENSOR_TYPES[sensor_type][1]
+        self._state = None
+
+    @property
+    def name(self):
+        """Return the name of the sensor."""
+        return self._tc_object.name + ' ' + SENSOR_TYPES[self.type][0]
+
+    @property
+    def icon(self):
+        """Icon to use in the frontend, if any."""
+        return SENSOR_TYPES[self.type][2]
+
+    @property
+    def state(self):
+        """Return the state of the device."""
+        return self._state
+
+    @property
+    def unit_of_measurement(self):
+        """Return the unit of measurement of this entity, if any."""
+        return self._unit_of_measurement
+
+    def update(self):
+        """Update the sensor."""
+        self._update_devices()
+
+        if self.type == 'battery':
+            self._state = self._tc_object.battery
+        elif self.type == 'state':
+            self._state = STATES[self._tc_object.status]
+        elif self.type == 'capacity':
+            self._state = self._tc_object.capacity
diff --git a/homeassistant/components/switch/thinkingcleaner.py b/homeassistant/components/switch/thinkingcleaner.py
new file mode 100644
index 0000000000000000000000000000000000000000..3bc4484db3880e5a266137f373d72f787acc6519
--- /dev/null
+++ b/homeassistant/components/switch/thinkingcleaner.py
@@ -0,0 +1,131 @@
+"""Support for ThinkingCleaner."""
+import time
+import logging
+from datetime import timedelta
+
+import homeassistant.util as util
+
+from homeassistant.const import (STATE_ON, STATE_OFF)
+from homeassistant.helpers.entity import ToggleEntity
+
+_LOGGER = logging.getLogger(__name__)
+
+REQUIREMENTS = ['https://github.com/TheRealLink/pythinkingcleaner'
+                '/archive/v0.0.2.zip'
+                '#pythinkingcleaner==0.0.2']
+
+MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
+MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(milliseconds=100)
+
+MIN_TIME_TO_WAIT = timedelta(seconds=5)
+MIN_TIME_TO_LOCK_UPDATE = 5
+
+SWITCH_TYPES = {
+    'clean': ['Clean', None, None],
+    'dock': ['Dock', None, None],
+    'find': ['Find', None, None],
+}
+
+
+def setup_platform(hass, config, add_devices, discovery_info=None):
+    """Setup the ThinkingCleaner platform."""
+    from pythinkingcleaner import Discovery
+
+    discovery = Discovery()
+    devices = discovery.discover()
+
+    @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
+    def update_devices():
+        """Update all devices."""
+        for device_object in devices:
+            device_object.update()
+
+    dev = []
+    for device in devices:
+        for type_name in SWITCH_TYPES.keys():
+            dev.append(ThinkingCleanerSwitch(device, type_name,
+                                             update_devices))
+
+    add_devices(dev)
+
+
+class ThinkingCleanerSwitch(ToggleEntity):
+    """ThinkingCleaner Switch (dock, clean, find me)."""
+
+    def __init__(self, tc_object, switch_type, update_devices):
+        """Initialize the ThinkingCleaner."""
+        self.type = switch_type
+
+        self._update_devices = update_devices
+        self._tc_object = tc_object
+        self._state = \
+            self._tc_object.is_cleaning if switch_type == 'clean' else False
+        self.lock = False
+        self.last_lock_time = None
+        self.graceful_state = False
+
+    def lock_update(self):
+        """Lock the update since TC clean takes some time to update."""
+        if self.is_update_locked():
+            return
+        self.lock = True
+        self.last_lock_time = time.time()
+
+    def reset_update_lock(self):
+        """Reset the update lock."""
+        self.lock = False
+        self.last_lock_time = None
+
+    def set_graceful_lock(self, state):
+        """Set the graceful state."""
+        self.graceful_state = state
+        self.reset_update_lock()
+        self.lock_update()
+
+    def is_update_locked(self):
+        """Check if the update method is locked."""
+        if self.last_lock_time is None:
+            return False
+
+        if time.time() - self.last_lock_time >= MIN_TIME_TO_LOCK_UPDATE:
+            self.last_lock_time = None
+            return False
+
+        return True
+
+    @property
+    def name(self):
+        """Return the name of the sensor."""
+        return self._tc_object.name + ' ' + SWITCH_TYPES[self.type][0]
+
+    @property
+    def is_on(self):
+        """Return true if device is on."""
+        if self.type == 'clean':
+            return self.graceful_state \
+                if self.is_update_locked() else self._tc_object.is_cleaning
+
+        return False
+
+    def turn_on(self, **kwargs):
+        """Turn the device on."""
+        if self.type == 'clean':
+            self.set_graceful_lock(True)
+            self._tc_object.start_cleaning()
+        elif self.type == 'dock':
+            self._tc_object.dock()
+        elif self.type == 'find':
+            self._tc_object.find_me()
+
+    def turn_off(self, **kwargs):
+        """Turn the device off."""
+        if self.type == 'clean':
+            self.set_graceful_lock(False)
+            self._tc_object.stop_cleaning()
+
+    def update(self):
+        """Update the switch state (Only for clean)."""
+        if self.type == 'clean' and not self.is_update_locked():
+            self._tc_object.update()
+            self._state = STATE_ON \
+                if self._tc_object.is_cleaning else STATE_OFF
diff --git a/homeassistant/components/zeroconf.py b/homeassistant/components/zeroconf.py
new file mode 100644
index 0000000000000000000000000000000000000000..bda6dcb153ba8e3086841b0ebf58aaaeafba67d6
--- /dev/null
+++ b/homeassistant/components/zeroconf.py
@@ -0,0 +1,50 @@
+"""
+This module exposes Home Assistant via Zeroconf.
+
+Zeroconf is also known as Bonjour, Avahi or Multicast DNS (mDNS).
+
+For more details about Zeroconf, please refer to the documentation at
+https://home-assistant.io/components/zeroconf/
+"""
+import logging
+import socket
+
+from homeassistant.const import (EVENT_HOMEASSISTANT_STOP, __version__)
+
+REQUIREMENTS = ["zeroconf==0.17.5"]
+
+DEPENDENCIES = ["api"]
+
+_LOGGER = logging.getLogger(__name__)
+
+DOMAIN = "zeroconf"
+
+ZEROCONF_TYPE = "_home-assistant._tcp.local."
+
+
+def setup(hass, config):
+    """Set up Zeroconf and make Home Assistant discoverable."""
+    from zeroconf import Zeroconf, ServiceInfo
+
+    zeroconf = Zeroconf()
+
+    zeroconf_name = "{}.{}".format(hass.config.location_name,
+                                   ZEROCONF_TYPE)
+
+    params = {"version": __version__, "base_url": hass.config.api.base_url,
+              "needs_auth": (hass.config.api.api_password is not None)}
+
+    info = ServiceInfo(ZEROCONF_TYPE, zeroconf_name,
+                       socket.inet_aton(hass.config.api.host),
+                       hass.config.api.port, 0, 0, params)
+
+    zeroconf.register_service(info)
+
+    def stop_zeroconf(event):
+        """Stop Zeroconf."""
+        zeroconf.unregister_service(info)
+        zeroconf.close()
+
+    hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_zeroconf)
+
+    return True
diff --git a/requirements_all.txt b/requirements_all.txt
index c1c678f4f4410f5e19fae17627cd1b1a0a8970fd..6b34aad3be1b58c6a60bcaf18508d0b00d2139af 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -76,6 +76,10 @@ https://github.com/HydrelioxGitHub/netatmo-api-python/archive/43ff238a0122b0939a
 # homeassistant.components.switch.dlink
 https://github.com/LinuxChristian/pyW215/archive/v0.1.1.zip#pyW215==0.1.1
 
+# homeassistant.components.sensor.thinkingcleaner
+# homeassistant.components.switch.thinkingcleaner
+https://github.com/TheRealLink/pythinkingcleaner/archive/v0.0.2.zip#pythinkingcleaner==0.0.2
+
 # homeassistant.components.alarm_control_panel.alarmdotcom
 https://github.com/Xorso/pyalarmdotcom/archive/0.1.1.zip#pyalarmdotcom==0.1.1
 
@@ -132,7 +136,7 @@ messagebird==1.1.1
 mficlient==0.3.0
 
 # homeassistant.components.discovery
-netdisco==0.6.2
+netdisco==0.6.4
 
 # homeassistant.components.sensor.neurio_energy
 neurio==0.2.10
@@ -313,3 +317,6 @@ xbee-helper==0.0.6
 
 # homeassistant.components.sensor.yr
 xmltodict
+
+# homeassistant.components.zeroconf
+zeroconf==0.17.5