diff --git a/homeassistant/components/device_tracker/fritz.py b/homeassistant/components/device_tracker/fritz.py
index c262a8fdf2a20f45381fc01b4e9c87ad80e2f38b..a11139afa0ffcf1190441bb04cc8d943016a6548 100644
--- a/homeassistant/components/device_tracker/fritz.py
+++ b/homeassistant/components/device_tracker/fritz.py
@@ -15,7 +15,7 @@ from homeassistant.components.device_tracker import (
 from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
 from homeassistant.util import Throttle
 
-REQUIREMENTS = ['fritzconnection==0.6']
+REQUIREMENTS = ['fritzconnection==0.6.3']
 
 # Return cached results if last scan was less then this time ago.
 MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
diff --git a/homeassistant/components/sensor/fritzbox_callmonitor.py b/homeassistant/components/sensor/fritzbox_callmonitor.py
index 9927b321024c0c57a4a4b937a988a087335dcb61..956ca3fe49586863a3d70e58ce3bfac37975e880 100644
--- a/homeassistant/components/sensor/fritzbox_callmonitor.py
+++ b/homeassistant/components/sensor/fritzbox_callmonitor.py
@@ -9,13 +9,19 @@ import socket
 import threading
 import datetime
 import time
+import re
 
 import voluptuous as vol
 
 from homeassistant.components.sensor import PLATFORM_SCHEMA
-from homeassistant.const import (CONF_HOST, CONF_PORT, CONF_NAME)
+from homeassistant.const import (CONF_HOST, CONF_PORT, CONF_NAME,
+                                 CONF_PASSWORD, CONF_USERNAME,
+                                 EVENT_HOMEASSISTANT_STOP)
 from homeassistant.helpers.entity import Entity
 import homeassistant.helpers.config_validation as cv
+from homeassistant.util import Throttle
+
+REQUIREMENTS = ['fritzconnection==0.6.3']
 
 _LOGGER = logging.getLogger(__name__)
 DEFAULT_NAME = 'Phone'
@@ -27,13 +33,24 @@ VALUE_RING = 'ringing'
 VALUE_CALL = 'dialing'
 VALUE_CONNECT = 'talking'
 VALUE_DISCONNECT = 'idle'
+CONF_PHONEBOOK = 'phonebook'
+CONF_PREFIXES = 'prefixes'
 
 INTERVAL_RECONNECT = 60
 
+# Return cached results if phonebook was downloaded less then this time ago.
+MIN_TIME_PHONEBOOK_UPDATE = datetime.timedelta(hours=6)
+SCAN_INTERVAL = datetime.timedelta(hours=3)
+
 PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
     vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
     vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
     vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
+    vol.Optional(CONF_PASSWORD, default='admin'): cv.string,
+    vol.Optional(CONF_USERNAME, default=''): cv.string,
+    vol.Optional(CONF_PHONEBOOK, default=0): cv.positive_int,
+    vol.Optional(CONF_PREFIXES, default=[]): vol.All(cv.ensure_list,
+                                                     [cv.string])
 })
 
 
@@ -42,14 +59,37 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
     name = config.get(CONF_NAME)
     host = config.get(CONF_HOST)
     port = config.get(CONF_PORT)
+    username = config.get(CONF_USERNAME)
+    password = config.get(CONF_PASSWORD)
+    phonebook_id = config.get('phonebook')
+    prefixes = config.get('prefixes')
+
+    try:
+        phonebook = FritzBoxPhonebook(host=host, port=port,
+                                      username=username, password=password,
+                                      phonebook_id=phonebook_id,
+                                      prefixes=prefixes)
+    # pylint: disable=bare-except
+    except:
+        phonebook = None
+        _LOGGER.warning('Phonebook with ID %s not found on Fritz!Box',
+                        phonebook_id)
 
-    sensor = FritzBoxCallSensor(name=name)
+    sensor = FritzBoxCallSensor(name=name, phonebook=phonebook)
 
     add_devices([sensor])
 
     monitor = FritzBoxCallMonitor(host=host, port=port, sensor=sensor)
     monitor.connect()
 
+    def _stop_listener(_event):
+        monitor.stopped.set()
+
+    hass.bus.listen_once(
+        EVENT_HOMEASSISTANT_STOP,
+        _stop_listener
+    )
+
     if monitor.sock is None:
         return False
     else:
@@ -59,11 +99,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
 class FritzBoxCallSensor(Entity):
     """Implementation of a Fritz!Box call monitor."""
 
-    def __init__(self, name):
+    def __init__(self, name, phonebook):
         """Initialize the sensor."""
         self._state = VALUE_DEFAULT
         self._attributes = {}
         self._name = name
+        self.phonebook = phonebook
 
     def set_state(self, state):
         """Set the state."""
@@ -75,8 +116,11 @@ class FritzBoxCallSensor(Entity):
 
     @property
     def should_poll(self):
-        """No polling needed."""
-        return False
+        """Polling needed only to update phonebook, if defined."""
+        if self.phonebook is None:
+            return False
+        else:
+            return True
 
     @property
     def state(self):
@@ -93,6 +137,18 @@ class FritzBoxCallSensor(Entity):
         """Return the state attributes."""
         return self._attributes
 
+    def number_to_name(self, number):
+        """Return a name for a given phone number."""
+        if self.phonebook is None:
+            return 'unknown'
+        else:
+            return self.phonebook.get_name(number)
+
+    def update(self):
+        """Update the phonebook if it is defined."""
+        if self.phonebook is not None:
+            self.phonebook.update_phonebook()
+
 
 class FritzBoxCallMonitor(object):
     """Event listener to monitor calls on the Fritz!Box."""
@@ -103,6 +159,7 @@ class FritzBoxCallMonitor(object):
         self.port = port
         self.sock = None
         self._sensor = sensor
+        self.stopped = threading.Event()
 
     def connect(self):
         """Connect to the Fritz!Box."""
@@ -110,7 +167,7 @@ class FritzBoxCallMonitor(object):
         self.sock.settimeout(10)
         try:
             self.sock.connect((self.host, self.port))
-            threading.Thread(target=self._listen, daemon=True).start()
+            threading.Thread(target=self._listen).start()
         except socket.error as err:
             self.sock = None
             _LOGGER.error("Cannot connect to %s on port %s: %s",
@@ -118,7 +175,7 @@ class FritzBoxCallMonitor(object):
 
     def _listen(self):
         """Listen to incoming or outgoing calls."""
-        while True:
+        while not self.stopped.isSet():
             try:
                 response = self.sock.recv(2048)
             except socket.timeout:
@@ -152,6 +209,7 @@ class FritzBoxCallMonitor(object):
                    "to": line[4],
                    "device": line[5],
                    "initiated": isotime}
+            att["from_name"] = self._sensor.number_to_name(att["from"])
             self._sensor.set_attributes(att)
         elif line[1] == "CALL":
             self._sensor.set_state(VALUE_CALL)
@@ -160,13 +218,73 @@ class FritzBoxCallMonitor(object):
                    "to": line[5],
                    "device": line[6],
                    "initiated": isotime}
+            att["to_name"] = self._sensor.number_to_name(att["to"])
             self._sensor.set_attributes(att)
         elif line[1] == "CONNECT":
             self._sensor.set_state(VALUE_CONNECT)
             att = {"with": line[4], "device": [3], "accepted": isotime}
+            att["with_name"] = self._sensor.number_to_name(att["with"])
             self._sensor.set_attributes(att)
         elif line[1] == "DISCONNECT":
             self._sensor.set_state(VALUE_DISCONNECT)
             att = {"duration": line[3], "closed": isotime}
             self._sensor.set_attributes(att)
         self._sensor.schedule_update_ha_state()
+
+
+class FritzBoxPhonebook(object):
+    """This connects to a FritzBox router and downloads its phone book."""
+
+    def __init__(self, host, port, username, password,
+                 phonebook_id=0, prefixes=None):
+        """Initialize the class."""
+        self.host = host
+        self.username = username
+        self.password = password
+        self.port = port
+        self.phonebook_id = phonebook_id
+        self.phonebook_dict = None
+        self.number_dict = None
+        self.prefixes = prefixes or []
+
+        # pylint: disable=import-error
+        import fritzconnection as fc
+        # Establish a connection to the FRITZ!Box.
+        self.fph = fc.FritzPhonebook(address=self.host,
+                                     user=self.username,
+                                     password=self.password)
+
+        if self.phonebook_id not in self.fph.list_phonebooks:
+            raise ValueError("Phonebook with this ID not found.")
+
+        self.update_phonebook()
+
+    @Throttle(MIN_TIME_PHONEBOOK_UPDATE)
+    def update_phonebook(self):
+        """Update the phone book dictionary."""
+        self.phonebook_dict = self.fph.get_all_names(self.phonebook_id)
+        self.number_dict = {re.sub(r'[^\d\+]', '', nr): name
+                            for name, nrs in self.phonebook_dict.items()
+                            for nr in nrs}
+        _LOGGER.info('Fritz!Box phone book successfully updated.')
+
+    def get_name(self, number):
+        """Return a name for a given phone number."""
+        number = re.sub(r'[^\d\+]', '', str(number))
+        if self.number_dict is None:
+            return 'unknown'
+        try:
+            return self.number_dict[number]
+        except KeyError:
+            pass
+        if self.prefixes:
+            for prefix in self.prefixes:
+                try:
+                    return self.number_dict[prefix + number]
+                except KeyError:
+                    pass
+                try:
+                    return self.number_dict[prefix + number.lstrip('0')]
+                except KeyError:
+                    pass
+        return 'unknown'
diff --git a/homeassistant/components/sensor/fritzbox_netmonitor.py b/homeassistant/components/sensor/fritzbox_netmonitor.py
index 82a0fa5fd9e0fcd5a7a689649cd1e07c6689e23a..ba0ee8be1b9d95e18b76454a5116a375ff17305f 100644
--- a/homeassistant/components/sensor/fritzbox_netmonitor.py
+++ b/homeassistant/components/sensor/fritzbox_netmonitor.py
@@ -17,7 +17,7 @@ from homeassistant.util import Throttle
 
 from requests.exceptions import RequestException
 
-REQUIREMENTS = ['fritzconnection==0.6']
+REQUIREMENTS = ['fritzconnection==0.6.3']
 
 MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=5)
 
diff --git a/requirements_all.txt b/requirements_all.txt
index 8d4a25eb69e034798d6f6ed5221674340ecd4fa2..750fd68a6032dee65e2fb672a458fee8c4c90fa3 100755
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -167,8 +167,9 @@ flux_led==0.15
 freesms==0.1.1
 
 # homeassistant.components.device_tracker.fritz
+# homeassistant.components.sensor.fritzbox_callmonitor
 # homeassistant.components.sensor.fritzbox_netmonitor
-# fritzconnection==0.6
+# fritzconnection==0.6.3
 
 # homeassistant.components.switch.fritzdect
 fritzhome==1.0.2