Add support for tracking devices on Netgear access points (#13331)

* Netgear: add support for tracking devices on access points

* Netgear: add SSL support and autodetection
pull/14242/head
Mathieu Velten 2018-05-02 15:38:24 +02:00 committed by Paulus Schoutsen
parent bf056b6f01
commit ce98dfe395
2 changed files with 84 additions and 21 deletions

View File

@ -12,21 +12,27 @@ import homeassistant.helpers.config_validation as cv
from homeassistant.components.device_tracker import ( from homeassistant.components.device_tracker import (
DOMAIN, PLATFORM_SCHEMA, DeviceScanner) DOMAIN, PLATFORM_SCHEMA, DeviceScanner)
from homeassistant.const import ( from homeassistant.const import (
CONF_HOST, CONF_PASSWORD, CONF_USERNAME, CONF_PORT) CONF_HOST, CONF_PASSWORD, CONF_USERNAME, CONF_PORT, CONF_SSL,
CONF_DEVICES, CONF_EXCLUDE)
REQUIREMENTS = ['pynetgear==0.3.3'] REQUIREMENTS = ['pynetgear==0.4.0']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
DEFAULT_HOST = 'routerlogin.net' CONF_APS = 'accesspoints'
DEFAULT_USER = 'admin'
DEFAULT_PORT = 5000
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string, vol.Optional(CONF_HOST, default=''): cv.string,
vol.Optional(CONF_USERNAME, default=DEFAULT_USER): cv.string, vol.Optional(CONF_SSL, default=False): cv.boolean,
vol.Optional(CONF_USERNAME, default=''): cv.string,
vol.Required(CONF_PASSWORD): cv.string, vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port vol.Optional(CONF_PORT, default=None): vol.Any(None, cv.port),
vol.Optional(CONF_DEVICES, default=[]):
vol.All(cv.ensure_list, [cv.string]),
vol.Optional(CONF_EXCLUDE, default=[]):
vol.All(cv.ensure_list, [cv.string]),
vol.Optional(CONF_APS, default=[]):
vol.All(cv.ensure_list, [cv.string]),
}) })
@ -34,11 +40,16 @@ def get_scanner(hass, config):
"""Validate the configuration and returns a Netgear scanner.""" """Validate the configuration and returns a Netgear scanner."""
info = config[DOMAIN] info = config[DOMAIN]
host = info.get(CONF_HOST) host = info.get(CONF_HOST)
ssl = info.get(CONF_SSL)
username = info.get(CONF_USERNAME) username = info.get(CONF_USERNAME)
password = info.get(CONF_PASSWORD) password = info.get(CONF_PASSWORD)
port = info.get(CONF_PORT) port = info.get(CONF_PORT)
devices = info.get(CONF_DEVICES)
excluded_devices = info.get(CONF_EXCLUDE)
accesspoints = info.get(CONF_APS)
scanner = NetgearDeviceScanner(host, username, password, port) scanner = NetgearDeviceScanner(host, ssl, username, password, port,
devices, excluded_devices, accesspoints)
return scanner if scanner.success_init else None return scanner if scanner.success_init else None
@ -46,16 +57,21 @@ def get_scanner(hass, config):
class NetgearDeviceScanner(DeviceScanner): class NetgearDeviceScanner(DeviceScanner):
"""Queries a Netgear wireless router using the SOAP-API.""" """Queries a Netgear wireless router using the SOAP-API."""
def __init__(self, host, username, password, port): def __init__(self, host, ssl, username, password, port, devices,
excluded_devices, accesspoints):
"""Initialize the scanner.""" """Initialize the scanner."""
import pynetgear import pynetgear
self.tracked_devices = devices
self.excluded_devices = excluded_devices
self.tracked_accesspoints = accesspoints
self.last_results = [] self.last_results = []
self._api = pynetgear.Netgear(password, host, username, port) self._api = pynetgear.Netgear(password, host, username, port, ssl)
_LOGGER.info("Logging in") _LOGGER.info("Logging in")
results = self._api.get_attached_devices() results = self.get_attached_devices()
self.success_init = results is not None self.success_init = results is not None
@ -68,15 +84,50 @@ class NetgearDeviceScanner(DeviceScanner):
"""Scan for new devices and return a list with found device IDs.""" """Scan for new devices and return a list with found device IDs."""
self._update_info() self._update_info()
return (device.mac for device in self.last_results) devices = []
for dev in self.last_results:
tracked = (not self.tracked_devices or
dev.mac in self.tracked_devices or
dev.name in self.tracked_devices)
tracked = tracked and (not self.excluded_devices or not(
dev.mac in self.excluded_devices or
dev.name in self.excluded_devices))
if tracked:
devices.append(dev.mac)
if (self.tracked_accesspoints and
dev.conn_ap_mac in self.tracked_accesspoints):
devices.append(dev.mac + "_" + dev.conn_ap_mac)
return devices
def get_device_name(self, device): def get_device_name(self, device):
"""Return the name of the given device or None if we don't know.""" """Return the name of the given device or the MAC if we don't know."""
try: parts = device.split("_")
return next(result.name for result in self.last_results mac = parts[0]
if result.mac == device) ap_mac = None
except StopIteration: if len(parts) > 1:
return None ap_mac = parts[1]
name = None
for dev in self.last_results:
if dev.mac == mac:
name = dev.name
break
if not name or name == "--":
name = mac
if ap_mac:
ap_name = "Router"
for dev in self.last_results:
if dev.mac == ap_mac:
ap_name = dev.name
break
return name + " on " + ap_name
return name
def _update_info(self): def _update_info(self):
"""Retrieve latest information from the Netgear router. """Retrieve latest information from the Netgear router.
@ -88,9 +139,21 @@ class NetgearDeviceScanner(DeviceScanner):
_LOGGER.info("Scanning") _LOGGER.info("Scanning")
results = self._api.get_attached_devices() results = self.get_attached_devices()
if results is None: if results is None:
_LOGGER.warning("Error scanning devices") _LOGGER.warning("Error scanning devices")
self.last_results = results or [] self.last_results = results or []
def get_attached_devices(self):
"""
List attached devices with pynetgear.
The v2 method takes more time and is more heavy on the router
so we only use it if we need connected AP info.
"""
if self.tracked_accesspoints:
return self._api.get_attached_devices_2()
return self._api.get_attached_devices()

View File

@ -869,7 +869,7 @@ pymysensors==0.11.1
pynello==1.5.1 pynello==1.5.1
# homeassistant.components.device_tracker.netgear # homeassistant.components.device_tracker.netgear
pynetgear==0.3.3 pynetgear==0.4.0
# homeassistant.components.switch.netio # homeassistant.components.switch.netio
pynetio==0.1.6 pynetio==0.1.6