From 9833b4b66337f6116147ed9b11f60ca8db2b7b04 Mon Sep 17 00:00:00 2001 From: caius <caius.cioran@gmail.com> Date: Wed, 9 Dec 2015 20:22:40 +0100 Subject: [PATCH] Add the fritz device tracker to track established connections to FritzBox routers --- .../components/device_tracker/fritz.py | 165 ++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 homeassistant/components/device_tracker/fritz.py diff --git a/homeassistant/components/device_tracker/fritz.py b/homeassistant/components/device_tracker/fritz.py new file mode 100644 index 00000000000..99c405d479f --- /dev/null +++ b/homeassistant/components/device_tracker/fritz.py @@ -0,0 +1,165 @@ +""" +homeassistant.components.device_tracker.fritz +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Unfortunately, you have to execute the following command by hand: +sudo apt-get install libxslt-dev libxml2-dev + +Device tracker platform that supports scanning a FitzBox router for device +presence. + +Configuration: + +To use the fritz tracker you have to adapt your configuration.yaml by +using the following template: + +device_tracker: + platform: fritz + host: YOUR_ROUTER_IP + username: YOUR_ADMIN_USERNAME + password: YOUR_ADMIN_PASSWORD + + +Description: + +host +*Optional +The IP address of your router, e.g. 192.168.0.1. +It is optional since every fritzbox is also reachable by using +the 169.254.1.1 IP. + +username +*Optional +The username of an user with administrative privileges, usually 'admin'. +However, it seems that it is not necessary to use it in +current generation fritzbox routers because the necessary data +can be retrieved anonymously. + +password +*Optional +The password for your given admin account. +However, it seems that it is not necessary to use it in current +generation fritzbox routers because the necessary data can +be retrieved anonymously. +""" + +import logging +from datetime import timedelta + +from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD +from homeassistant.helpers import validate_config +from homeassistant.util import Throttle +from homeassistant.components.device_tracker import DOMAIN + +# Return cached results if last scan was less then this time ago +MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5) + +_LOGGER = logging.getLogger(__name__) + + +# noinspection PyUnusedLocal +def get_scanner(hass, config): + """ + Validates config and returns FritzBoxScanner + @param hass: + @param config: + @return: + """ + if not validate_config(config, + {DOMAIN: []}, + _LOGGER): + return None + scanner = FritzBoxScanner(config[DOMAIN]) + return scanner if scanner.success_init else None + + +# pylint: disable=too-many-instance-attributes +class FritzBoxScanner(object): + """ + This class queries a FritzBox router. It is using the + fritzconnection library for communication with the router. + + The API description can be found under: + https://pypi.python.org/pypi/fritzconnection/0.4.6 + + This scanner retrieves the list of known hosts and checks + their corresponding states (on, or off). + + Due to a bug of the fritzbox api (router side) it is not possible + to track more than 16 hosts. + """ + def __init__(self, config): + self.last_results = [] + self.host = '169.254.1.1' # This IP is valid for all fritzboxes + self.username = 'admin' + self.password = '' + self.success_init = True + + # Try to import the fritzconnection library + try: + # noinspection PyPackageRequirements,PyUnresolvedReferences + import fritzconnection as fc + except ImportError: + _LOGGER.exception("""Failed to import Python library + fritzconnection. Please run + <home-assistant>/setup to install it.""") + self.success_init = False + return + + # Check for user specific configuration + if CONF_HOST in config.keys(): + self.host = config[CONF_HOST] + if CONF_USERNAME in config.keys(): + self.username = config[CONF_USERNAME] + if CONF_PASSWORD in config.keys(): + self.password = config[CONF_PASSWORD] + + # Establish a connection to the fritzbox + # noinspection PyBroadException + try: + self.fritz_box = fc.FritzHosts(address=self.host, + user=self.username, + password=self.password) + except Exception: + self.fritz_box = None + + # At this point it is difficult to tell if a connection is established. + # So just check for null objects ... + if self.fritz_box is None or not self.fritz_box.modelname: + self.success_init = False + + if self.success_init: + _LOGGER.info("Successfully connected to {0}" + .format(self.fritz_box.modelname)) + self._update_info() + else: + _LOGGER.error("Failed to establish connection to FritzBox " + "with IP: {0}".format(self.host)) + + def scan_devices(self): + """ Scan for new devices and return a list of found device ids. """ + self._update_info() + active_hosts = [] + for known_host in self.last_results: + if known_host["status"] == "1": + active_hosts.append(known_host["mac"]) + return active_hosts + + def get_device_name(self, mac): + """ Returns the name of the given device or None if is not known. """ + ret = self.fritz_box.get_specific_host_entry(mac)["NewHostName"] + if ret == {}: + return None + return ret + + @Throttle(MIN_TIME_BETWEEN_SCANS) + def _update_info(self): + """ + Retrieves latest information from the FritzBox. + Returns boolean if scanning successful. + """ + if not self.success_init: + return False + + _LOGGER.info("Scanning") + self.last_results = self.fritz_box.get_hosts_info() + return True -- GitLab