Advertisement
touliloup

MiRouter.py

Jan 11th, 2017
257
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.71 KB | None | 0 0
  1. """
  2. Support for Xiaomi Mi routers.
  3.  
  4. For more details about this platform, please refer to the documentation at
  5. https://home-assistant.io/components/device_tracker.mirouter/
  6. """
  7. import logging
  8. import threading
  9. from datetime import timedelta
  10.  
  11. import requests
  12. import voluptuous as vol
  13.  
  14. import homeassistant.helpers.config_validation as cv
  15. from homeassistant.components.device_tracker import (
  16.     DOMAIN, PLATFORM_SCHEMA, DeviceScanner)
  17. from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
  18. from homeassistant.util import Throttle
  19.  
  20. # Return cached results if last scan was less then this time ago.
  21. MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
  22.  
  23. _LOGGER = logging.getLogger(__name__)
  24.  
  25. PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
  26.     vol.Required(CONF_HOST): cv.string,
  27.     vol.Required(CONF_USERNAME): cv.string,
  28.     vol.Required(CONF_PASSWORD): cv.string
  29. })
  30.  
  31.  
  32. def get_scanner(hass, config):
  33.     """Validate the configuration and return a Mi scanner."""
  34.     scanner = MiDeviceScanner(config[DOMAIN])
  35.  
  36.     return scanner if scanner.success_init else None
  37.  
  38.  
  39. class MiDeviceScanner(DeviceScanner):
  40.     """This class queries a Xiaomi Mi router.
  41.  
  42.    Adapted from Luci scanner.
  43.    """
  44.  
  45.     def __init__(self, config):
  46.         """Initialize the scanner."""
  47.         host = config[CONF_HOST]
  48.         username, password = config[CONF_USERNAME], config[CONF_PASSWORD]
  49.  
  50.         self.lock = threading.Lock()
  51.  
  52.         self.last_results = {}
  53.  
  54.         self.token = _get_token(host, username, password)
  55.         self.host = host
  56.  
  57.         self.mac2name = None
  58.         self.success_init = self.token is not None
  59.  
  60.     def scan_devices(self):
  61.         """Scan for new devices and return a list with found device IDs."""
  62.         self._update_info()
  63.         return self.last_results
  64.  
  65.     def get_device_name(self, device):
  66.         """Return the name of the given device or None if we don't know."""
  67.         with self.lock:
  68.             if self.mac2name is None:
  69.                 url = "http://{}/cgi-bin/luci/;stok={}/api/misystem/devicelist".format(self.host, self.token)
  70.                 result = _get_device_list(url)
  71.                 if result:
  72.                     hosts = [x for x in result.values()
  73.                              if 'mac' in x and 'name' in x]
  74.                     mac2name_list = [
  75.                         (x['mac'].upper(), x['name']) for x in hosts]
  76.                     self.mac2name = dict(mac2name_list)
  77.                 else:
  78.                     # Error, handled in the _req_json_rpc
  79.                     return
  80.             return self.mac2name.get(device.upper(), None)
  81.  
  82.     @Throttle(MIN_TIME_BETWEEN_SCANS)
  83.     def _update_info(self):
  84.         """Ensure the informations from the router are up to date.
  85.  
  86.        Returns true if scanning successful.
  87.        """
  88.         if not self.success_init:
  89.             return False
  90.  
  91.         with self.lock:
  92.             _LOGGER.info('Refreshing device list')
  93.             url = "http://{}/cgi-bin/luci/;stok={}/api/misystem/devicelist".format(self.host, self.token)
  94.             result = _get_device_list(url)
  95.             if result:
  96.                 self.last_results = []
  97.                 for device_entry in result:
  98.                     # Check if the device is marked as connected
  99.                     if int(device_entry['online']) == 1:
  100.                         self.last_results.append(device_entry['mac'])
  101.  
  102.                 return True
  103.  
  104.             return False
  105.  
  106.  
  107. def _get_device_list(url, **kwargs):
  108.     try:
  109.         res = requests.get(url, timeout=5, **kwargs)
  110.     except requests.exceptions.Timeout:
  111.         _LOGGER.exception('Connection to the router timed out')
  112.         return
  113.     return _extract_result(res, 'list')
  114.  
  115.  
  116. def _get_token(host, username, password):
  117.     """Get authentication token for the given host+username+password."""
  118.     url = 'http://{}/cgi-bin/luci/api/xqsystem/login'.format(host)
  119.     data = {'username': username, 'password': password}
  120.     try:
  121.         res = requests.post(url, data=data, timeout=5)
  122.     except requests.exceptions.Timeout:
  123.         _LOGGER.exception('Connection to the router timed out')
  124.         return
  125.     return _extract_result(res, 'token')
  126.  
  127.  
  128. def _extract_result(res, keyName):
  129.     if res.status_code == 200:
  130.         try:
  131.             result = res.json()
  132.         except ValueError:
  133.             # If json decoder could not parse the response
  134.             _LOGGER.exception('Failed to parse response from mi router')
  135.             return
  136.         try:
  137.             return result[keyName]
  138.         except KeyError:
  139.             _LOGGER.exception('No %s in response from mi router. %s', keyName, result)
  140.             return
  141.     else:
  142.         _LOGGER.error('Invalid response from mi router: %s', res)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement