Add support for tracking devices on Netgear access points (#13331)
* Netgear: add support for tracking devices on access points * Netgear: add SSL support and autodetectionpull/14242/head
parent
bf056b6f01
commit
ce98dfe395
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue