Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed NDMS for latest firmware #15511

Merged
merged 5 commits into from
Jul 31, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 35 additions & 53 deletions homeassistant/components/device_tracker/keenetic_ndms2.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@
https://home-assistant.io/components/device_tracker.keenetic_ndms2/
"""
import logging
from collections import namedtuple

import requests
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_HOST, CONF_PORT, CONF_PASSWORD, CONF_USERNAME
)

REQUIREMENTS = ['ndms2_client==0.0.3']

_LOGGER = logging.getLogger(__name__)

# Interface name to track devices for. Most likely one will not need to
Expand All @@ -25,11 +25,13 @@
CONF_INTERFACE = 'interface'

DEFAULT_INTERFACE = 'Home'
DEFAULT_PORT = 23


PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_INTERFACE, default=DEFAULT_INTERFACE): cv.string,
})
Expand All @@ -42,21 +44,22 @@ def get_scanner(_hass, config):
return scanner if scanner.success_init else None


Device = namedtuple('Device', ['mac', 'name'])


class KeeneticNDMS2DeviceScanner(DeviceScanner):
"""This class scans for devices using keenetic NDMS2 web interface."""

def __init__(self, config):
"""Initialize the scanner."""
from ndms2_client import Client, TelnetConnection
self.last_results = []

self._url = 'http://%s/rci/show/ip/arp' % config[CONF_HOST]
self._interface = config[CONF_INTERFACE]

self._username = config.get(CONF_USERNAME)
self._password = config.get(CONF_PASSWORD)
self._client = Client(TelnetConnection(
config.get(CONF_HOST),
config.get(CONF_PORT),
config.get(CONF_USERNAME),
config.get(CONF_PASSWORD),
))

self.success_init = self._update_info()
_LOGGER.info("Scanner initialized")
Expand All @@ -69,53 +72,32 @@ def scan_devices(self):

def get_device_name(self, device):
"""Return the name of the given device or None if we don't know."""
filter_named = [result.name for result in self.last_results
if result.mac == device]

if filter_named:
return filter_named[0]
return None
name = next((
result.name for result in self.last_results
if result.mac == device), None)
return name

def get_extra_attributes(self, device):
"""Return the IP of the given device."""
attributes = next((
{'ip': result.ip} for result in self.last_results
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we using 'ip' elsewhere as attribute name? I'd prefer to use 'ip_address'.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we are: homeassistant.components.device_tracker.nmap_tracker.NmapDeviceScanner#get_extra_attributes
There is also homeassistant.components.device_tracker.unifi.UnifiScanner#get_extra_attributes but the contents of this dict are not known from the sigratures - this component passes JSON response from API with no changes and explanations

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok let's keep it as is then.

if result.mac == device), {})
return attributes

def _update_info(self):
"""Get ARP from keenetic router."""
_LOGGER.info("Fetching...")

last_results = []
_LOGGER.debug("Fetching devices from router...")

# doing a request
try:
from requests.auth import HTTPDigestAuth
res = requests.get(self._url, timeout=10, auth=HTTPDigestAuth(
self._username, self._password
))
except requests.exceptions.Timeout:
_LOGGER.error(
"Connection to the router timed out at URL %s", self._url)
return False
if res.status_code != 200:
_LOGGER.error(
"Connection failed with http code %s", res.status_code)
return False
from ndms2_client import ConnectionException
try:
result = res.json()
except ValueError:
# If json decoder could not parse the response
_LOGGER.error("Failed to parse response from router")
self.last_results = [
dev
for dev in self._client.get_devices()
if dev.interface == self._interface
]
_LOGGER.debug("Successfully fetched data from router")
return True

except ConnectionException:
_LOGGER.error("Error fetching data from router")
return False

# parsing response
for info in result:
if info.get('interface') != self._interface:
continue
mac = info.get('mac')
name = info.get('name')
# No address = no item :)
if mac is None:
continue

last_results.append(Device(mac.upper(), name))

self.last_results = last_results

_LOGGER.info("Request successful")
return True
3 changes: 3 additions & 0 deletions requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,9 @@ nad_receiver==0.0.9
# homeassistant.components.light.nanoleaf_aurora
nanoleaf==0.4.1

# homeassistant.components.device_tracker.keenetic_ndms2
ndms2_client==0.0.3

# homeassistant.components.sensor.netdata
netdata==0.1.2

Expand Down