2019-04-03 15:40:03 +00:00
|
|
|
"""Support for Netgear routers."""
|
2014-11-12 05:39:17 +00:00
|
|
|
import logging
|
2020-02-16 22:27:19 +00:00
|
|
|
from pprint import pformat
|
2014-11-12 05:39:17 +00:00
|
|
|
|
2019-10-17 13:05:14 +00:00
|
|
|
from pynetgear import Netgear
|
2016-09-05 17:37:36 +00:00
|
|
|
import voluptuous as vol
|
|
|
|
|
2017-01-02 19:50:42 +00:00
|
|
|
from homeassistant.components.device_tracker import (
|
2019-07-31 19:25:30 +00:00
|
|
|
DOMAIN,
|
|
|
|
PLATFORM_SCHEMA,
|
|
|
|
DeviceScanner,
|
|
|
|
)
|
2016-09-05 17:37:36 +00:00
|
|
|
from homeassistant.const import (
|
2019-12-09 13:46:24 +00:00
|
|
|
CONF_DEVICES,
|
|
|
|
CONF_EXCLUDE,
|
2019-07-31 19:25:30 +00:00
|
|
|
CONF_HOST,
|
|
|
|
CONF_PASSWORD,
|
|
|
|
CONF_PORT,
|
|
|
|
CONF_SSL,
|
2019-12-09 13:46:24 +00:00
|
|
|
CONF_USERNAME,
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2019-12-09 13:46:24 +00:00
|
|
|
import homeassistant.helpers.config_validation as cv
|
2014-11-12 05:39:17 +00:00
|
|
|
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
2017-04-30 05:04:49 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
CONF_APS = "accesspoints"
|
2016-09-05 17:37:36 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
|
|
|
{
|
|
|
|
vol.Optional(CONF_HOST, default=""): cv.string,
|
|
|
|
vol.Optional(CONF_SSL, default=False): cv.boolean,
|
|
|
|
vol.Optional(CONF_USERNAME, default=""): cv.string,
|
|
|
|
vol.Required(CONF_PASSWORD): cv.string,
|
2020-02-16 22:27:19 +00:00
|
|
|
vol.Optional(CONF_PORT): cv.port,
|
2019-07-31 19:25:30 +00:00
|
|
|
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]),
|
|
|
|
}
|
|
|
|
)
|
2016-09-05 17:37:36 +00:00
|
|
|
|
2014-11-12 05:39:17 +00:00
|
|
|
|
|
|
|
def get_scanner(hass, config):
|
2016-03-07 20:18:53 +00:00
|
|
|
"""Validate the configuration and returns a Netgear scanner."""
|
2015-01-17 21:49:22 +00:00
|
|
|
info = config[DOMAIN]
|
2020-02-16 22:27:19 +00:00
|
|
|
host = info[CONF_HOST]
|
|
|
|
ssl = info[CONF_SSL]
|
|
|
|
username = info[CONF_USERNAME]
|
|
|
|
password = info[CONF_PASSWORD]
|
2016-04-10 03:29:06 +00:00
|
|
|
port = info.get(CONF_PORT)
|
2020-02-16 22:27:19 +00:00
|
|
|
devices = info[CONF_DEVICES]
|
|
|
|
excluded_devices = info[CONF_EXCLUDE]
|
|
|
|
accesspoints = info[CONF_APS]
|
2015-08-24 00:20:09 +00:00
|
|
|
|
2020-02-16 22:27:19 +00:00
|
|
|
api = Netgear(password, host, username, port, ssl)
|
|
|
|
scanner = NetgearDeviceScanner(api, devices, excluded_devices, accesspoints)
|
2014-11-12 05:39:17 +00:00
|
|
|
|
2020-02-16 22:27:19 +00:00
|
|
|
_LOGGER.debug("Logging in")
|
|
|
|
|
|
|
|
results = scanner.get_attached_devices()
|
|
|
|
|
|
|
|
if results is not None:
|
|
|
|
scanner.last_results = results
|
|
|
|
else:
|
|
|
|
_LOGGER.error("Failed to Login")
|
|
|
|
return None
|
|
|
|
|
|
|
|
return scanner
|
2014-11-12 05:39:17 +00:00
|
|
|
|
|
|
|
|
2017-01-02 19:50:42 +00:00
|
|
|
class NetgearDeviceScanner(DeviceScanner):
|
2016-03-07 17:12:06 +00:00
|
|
|
"""Queries a Netgear wireless router using the SOAP-API."""
|
2016-03-07 20:18:53 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
def __init__(
|
2020-02-16 22:27:19 +00:00
|
|
|
self, api, devices, excluded_devices, accesspoints,
|
2019-07-31 19:25:30 +00:00
|
|
|
):
|
2016-03-07 20:18:53 +00:00
|
|
|
"""Initialize the scanner."""
|
2018-05-02 13:38:24 +00:00
|
|
|
self.tracked_devices = devices
|
|
|
|
self.excluded_devices = excluded_devices
|
|
|
|
self.tracked_accesspoints = accesspoints
|
2015-07-20 06:44:32 +00:00
|
|
|
self.last_results = []
|
2020-02-16 22:27:19 +00:00
|
|
|
self._api = api
|
2014-11-12 05:39:17 +00:00
|
|
|
|
|
|
|
def scan_devices(self):
|
2016-03-07 20:18:53 +00:00
|
|
|
"""Scan for new devices and return a list with found device IDs."""
|
2014-11-12 05:39:17 +00:00
|
|
|
self._update_info()
|
|
|
|
|
2018-05-02 13:38:24 +00:00
|
|
|
devices = []
|
|
|
|
|
|
|
|
for dev in self.last_results:
|
2019-07-31 19:25:30 +00:00
|
|
|
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
|
|
|
|
)
|
|
|
|
)
|
2020-02-15 16:21:04 +00:00
|
|
|
if tracked:
|
2018-05-02 13:38:24 +00:00
|
|
|
devices.append(dev.mac)
|
2019-07-31 19:25:30 +00:00
|
|
|
if (
|
|
|
|
self.tracked_accesspoints
|
|
|
|
and dev.conn_ap_mac in self.tracked_accesspoints
|
|
|
|
):
|
2020-01-03 13:47:06 +00:00
|
|
|
devices.append(f"{dev.mac}_{dev.conn_ap_mac}")
|
2018-05-02 13:38:24 +00:00
|
|
|
|
|
|
|
return devices
|
2014-11-12 05:39:17 +00:00
|
|
|
|
2018-02-11 17:20:28 +00:00
|
|
|
def get_device_name(self, device):
|
2018-05-02 13:38:24 +00:00
|
|
|
"""Return the name of the given device or the MAC if we don't know."""
|
|
|
|
parts = device.split("_")
|
|
|
|
mac = parts[0]
|
|
|
|
ap_mac = None
|
|
|
|
if len(parts) > 1:
|
|
|
|
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
|
|
|
|
|
2020-01-03 13:47:06 +00:00
|
|
|
return f"{name} on {ap_name}"
|
2018-05-02 13:38:24 +00:00
|
|
|
|
|
|
|
return name
|
2014-11-12 05:39:17 +00:00
|
|
|
|
|
|
|
def _update_info(self):
|
2016-03-07 20:18:53 +00:00
|
|
|
"""Retrieve latest information from the Netgear router.
|
|
|
|
|
2015-09-07 17:19:11 +00:00
|
|
|
Returns boolean if scanning successful.
|
|
|
|
"""
|
2020-02-16 22:27:19 +00:00
|
|
|
_LOGGER.debug("Scanning")
|
2014-11-12 05:39:17 +00:00
|
|
|
|
2018-05-02 13:38:24 +00:00
|
|
|
results = self.get_attached_devices()
|
2016-03-15 04:12:42 +00:00
|
|
|
|
2020-02-16 22:27:19 +00:00
|
|
|
if _LOGGER.isEnabledFor(logging.DEBUG):
|
|
|
|
_LOGGER.debug("Scan result: \n%s", pformat(results))
|
|
|
|
|
2017-07-24 14:45:02 +00:00
|
|
|
if results is None:
|
|
|
|
_LOGGER.warning("Error scanning devices")
|
2016-03-15 04:12:42 +00:00
|
|
|
|
2017-07-24 14:45:02 +00:00
|
|
|
self.last_results = results or []
|
2018-05-02 13:38:24 +00:00
|
|
|
|
|
|
|
def get_attached_devices(self):
|
2020-02-16 22:27:19 +00:00
|
|
|
"""List attached devices with pynetgear.
|
2018-05-02 13:38:24 +00:00
|
|
|
|
|
|
|
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()
|