From e4ebae55d50daee6e901abdb602eac0bb28a7d80 Mon Sep 17 00:00:00 2001
From: LvivEchoes <pavlo.tsiupka@gmail.com>
Date: Sun, 30 Apr 2017 06:39:11 +0300
Subject: [PATCH] Feature/add mikrotik device tracker (#7366)

* Add Mikroik device tracker platform

* Update coveragerc with mikrotik.py

* Update coveragerc with mikrotik.py

* Fix lint errors
---
 .coveragerc                                   |   1 +
 .../components/device_tracker/mikrotik.py     | 131 ++++++++++++++++++
 requirements_all.txt                          |   3 +
 3 files changed, 135 insertions(+)
 create mode 100644 homeassistant/components/device_tracker/mikrotik.py

diff --git a/.coveragerc b/.coveragerc
index 365a3a23fc1..90caadc23b5 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -205,6 +205,7 @@ omit =
     homeassistant/components/device_tracker/icloud.py
     homeassistant/components/device_tracker/linksys_ap.py
     homeassistant/components/device_tracker/luci.py
+    homeassistant/components/device_tracker/mikrotik.py
     homeassistant/components/device_tracker/netgear.py
     homeassistant/components/device_tracker/nmap_tracker.py
     homeassistant/components/device_tracker/ping.py
diff --git a/homeassistant/components/device_tracker/mikrotik.py b/homeassistant/components/device_tracker/mikrotik.py
new file mode 100644
index 00000000000..5b28a9eb664
--- /dev/null
+++ b/homeassistant/components/device_tracker/mikrotik.py
@@ -0,0 +1,131 @@
+"""
+Support for Mikrotik routers as device tracker.
+
+For more details about this platform, please refer to the documentation at
+https://home-assistant.io/components/device_tracker.mikrotik/
+"""
+import logging
+import threading
+from datetime import timedelta
+
+import voluptuous as vol
+
+import homeassistant.helpers.config_validation as cv
+from homeassistant.components.device_tracker import (
+    DOMAIN, PLATFORM_SCHEMA, DeviceScanner)
+from homeassistant.const import (CONF_HOST,
+                                 CONF_PASSWORD,
+                                 CONF_USERNAME,
+                                 CONF_PORT)
+from homeassistant.util import Throttle
+
+REQUIREMENTS = ['librouteros==1.0.2']
+
+# Return cached results if last scan was less then this time ago.
+MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
+
+MTK_DEFAULT_API_PORT = '8728'
+
+_LOGGER = logging.getLogger(__name__)
+
+PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
+    vol.Required(CONF_HOST): cv.string,
+    vol.Required(CONF_USERNAME): cv.string,
+    vol.Required(CONF_PASSWORD): cv.string,
+    vol.Optional(CONF_PORT, default=MTK_DEFAULT_API_PORT): cv.port
+})
+
+
+def get_scanner(hass, config):
+    """Validate the configuration and return MTikScanner."""
+    scanner = MikrotikScanner(config[DOMAIN])
+    return scanner if scanner.success_init else None
+
+
+class MikrotikScanner(DeviceScanner):
+    """This class queries a Mikrotik router."""
+
+    def __init__(self, config):
+        """Initialize the scanner."""
+        self.last_results = {}
+
+        self.host = config[CONF_HOST]
+        self.port = config[CONF_PORT]
+        self.username = config[CONF_USERNAME]
+        self.password = config[CONF_PASSWORD]
+
+        self.lock = threading.Lock()
+
+        self.connected = False
+        self.success_init = False
+        self.client = None
+
+        self.success_init = self.connect_to_device()
+
+        if self.success_init:
+            _LOGGER.info('Start polling Mikrotik router...')
+            self._update_info()
+        else:
+            _LOGGER.error('Connection to Mikrotik failed.')
+
+    def connect_to_device(self):
+        """Connect to Mikrotik method."""
+        # pylint: disable=import-error
+        import librouteros
+        try:
+            self.client = librouteros.connect(
+                self.host,
+                self.username,
+                self.password,
+                port=int(self.port)
+            )
+
+            routerboard_info = self.client(cmd='/system/routerboard/getall')
+
+            if routerboard_info:
+                _LOGGER.info('Connected to Mikrotik %s with ip %s.',
+                             routerboard_info[0].get('model', 'Router'),
+                             self.host)
+                self.connected = True
+
+        except (librouteros.exceptions.TrapError,
+                librouteros.exceptions.ConnectionError) as api_error:
+            _LOGGER.error('Connection error: %s', api_error)
+
+        return self.connected
+
+    def scan_devices(self):
+        """Scan for new devices and return a list with found device MACs."""
+        self._update_info()
+        return [device for device in self.last_results]
+
+    def get_device_name(self, mac):
+        """Return the name of the given device or None if we don't know."""
+        with self.lock:
+            return self.last_results.get(mac)
+
+    @Throttle(MIN_TIME_BETWEEN_SCANS)
+    def _update_info(self):
+        """Retrieve latest information from the Mikrotik box."""
+        with self.lock:
+            _LOGGER.info('Loading wireless device from Mikrotik...')
+
+            wireless_clients = self.client(
+                cmd='/interface/wireless/registration-table/getall'
+            )
+            device_names = self.client(cmd='/ip/dhcp-server/lease/getall')
+
+            if device_names is None or wireless_clients is None:
+                return False
+
+            mac_names = {device.get('mac-address'): device.get('host-name')
+                         for device in device_names
+                         if device.get('mac-address')}
+
+            self.last_results = {
+                device.get('mac-address'):
+                    mac_names.get(device.get('mac-address'))
+                for device in wireless_clients
+            }
+
+            return True
diff --git a/requirements_all.txt b/requirements_all.txt
index 4cde496c7cd..8b5ad9bc775 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -363,6 +363,9 @@ knxip==0.3.3
 # homeassistant.components.device_tracker.owntracks
 libnacl==1.5.0
 
+# homeassistant.components.device_tracker.mikrotik
+librouteros==1.0.2
+
 # homeassistant.components.media_player.soundtouch
 libsoundtouch==0.3.0
 
-- 
GitLab