ASUSWRT Autodetect protocol (#2300)

This commit is contained in:
Per Sandström 2016-06-15 07:17:32 +02:00 committed by Paulus Schoutsen
parent 39913075f4
commit 38030fcfca

View File

@ -6,8 +6,10 @@ https://home-assistant.io/components/device_tracker.asuswrt/
""" """
import logging import logging
import re import re
import socket
import telnetlib import telnetlib
import threading import threading
from collections import namedtuple
from datetime import timedelta from datetime import timedelta
from homeassistant.components.device_tracker import DOMAIN from homeassistant.components.device_tracker import DOMAIN
@ -68,6 +70,8 @@ def get_scanner(hass, config):
return scanner if scanner.success_init else None return scanner if scanner.success_init else None
AsusWrtResult = namedtuple('AsusWrtResult', 'neighbors leases arp')
class AsusWrtDeviceScanner(object): class AsusWrtDeviceScanner(object):
"""This class queries a router running ASUSWRT firmware.""" """This class queries a router running ASUSWRT firmware."""
@ -130,7 +134,7 @@ class AsusWrtDeviceScanner(object):
def ssh_connection(self): def ssh_connection(self):
"""Retrieve data from ASUSWRT via the ssh protocol.""" """Retrieve data from ASUSWRT via the ssh protocol."""
from pexpect import pxssh from pexpect import pxssh, exceptions
try: try:
ssh = pxssh.pxssh() ssh = pxssh.pxssh()
@ -140,7 +144,7 @@ class AsusWrtDeviceScanner(object):
ssh.login(self.host, self.username, self.password) ssh.login(self.host, self.username, self.password)
else: else:
_LOGGER.error('No password or public key specified') _LOGGER.error('No password or public key specified')
return('', '', '') return None
ssh.sendline(_IP_NEIGH_CMD) ssh.sendline(_IP_NEIGH_CMD)
ssh.prompt() ssh.prompt()
neighbors = ssh.before.split(b'\n')[1:-1] neighbors = ssh.before.split(b'\n')[1:-1]
@ -157,10 +161,13 @@ class AsusWrtDeviceScanner(object):
ssh.prompt() ssh.prompt()
leases_result = ssh.before.split(b'\n')[1:-1] leases_result = ssh.before.split(b'\n')[1:-1]
ssh.logout() ssh.logout()
return (neighbors, leases_result, arp_result) return AsusWrtResult(neighbors, leases_result, arp_result)
except pxssh.ExceptionPxssh as exc: except pxssh.ExceptionPxssh as exc:
_LOGGER.exception('Unexpected response from router: %s', exc) _LOGGER.error('Unexpected response from router: %s', exc)
return ('', '', '') return None
except exceptions.EOF:
_LOGGER.error('Connection refused or no route to host')
return None
def telnet_connection(self): def telnet_connection(self):
"""Retrieve data from ASUSWRT via the telnet protocol.""" """Retrieve data from ASUSWRT via the telnet protocol."""
@ -186,25 +193,42 @@ class AsusWrtDeviceScanner(object):
leases_result = (telnet.read_until(prompt_string). leases_result = (telnet.read_until(prompt_string).
split(b'\n')[1:-1]) split(b'\n')[1:-1])
telnet.write('exit\n'.encode('ascii')) telnet.write('exit\n'.encode('ascii'))
return (neighbors, leases_result, arp_result) return AsusWrtResult(neighbors, leases_result, arp_result)
except EOFError: except EOFError:
_LOGGER.exception("Unexpected response from router") _LOGGER.error("Unexpected response from router")
return ('', '', '') return None
except ConnectionRefusedError: except ConnectionRefusedError:
_LOGGER.exception("Connection refused by router," _LOGGER.error("Connection refused by router, is telnet enabled?")
" is telnet enabled?") return None
return ('', '', '') except socket.gaierror as exc:
_LOGGER.error("Socket exception: %s", exc)
return None
except OSError as exc:
_LOGGER.error("OSError: %s", exc)
return None
def get_asuswrt_data(self): def get_asuswrt_data(self):
"""Retrieve data from ASUSWRT and return parsed result.""" """Retrieve data from ASUSWRT and return parsed result."""
if self.protocol == 'telnet': if self.protocol == 'ssh':
neighbors, leases_result, arp_result = self.telnet_connection() result = self.ssh_connection()
elif self.protocol == 'telnet':
result = self.telnet_connection()
else: else:
neighbors, leases_result, arp_result = self.ssh_connection() # autodetect protocol
result = self.ssh_connection()
if result:
self.protocol = 'ssh'
else:
result = self.telnet_connection()
if result:
self.protocol = 'telnet'
if not result:
return {}
devices = {} devices = {}
if self.mode == 'ap': if self.mode == 'ap':
for lease in leases_result: for lease in result.leases:
match = _WL_REGEX.search(lease.decode('utf-8')) match = _WL_REGEX.search(lease.decode('utf-8'))
if not match: if not match:
@ -214,7 +238,7 @@ class AsusWrtDeviceScanner(object):
host = '' host = ''
# match mac addresses to IP addresses in ARP table # match mac addresses to IP addresses in ARP table
for arp in arp_result: for arp in result.arp:
if match.group('mac').lower() in arp.decode('utf-8'): if match.group('mac').lower() in arp.decode('utf-8'):
arp_match = _ARP_REGEX.search(arp.decode('utf-8')) arp_match = _ARP_REGEX.search(arp.decode('utf-8'))
if not arp_match: if not arp_match:
@ -228,7 +252,7 @@ class AsusWrtDeviceScanner(object):
'mac': match.group('mac').upper(), 'mac': match.group('mac').upper(),
} }
else: else:
for lease in leases_result: for lease in result.leases:
match = _LEASES_REGEX.search(lease.decode('utf-8')) match = _LEASES_REGEX.search(lease.decode('utf-8'))
if not match: if not match:
@ -248,7 +272,7 @@ class AsusWrtDeviceScanner(object):
'mac': match.group('mac').upper(), 'mac': match.group('mac').upper(),
} }
for neighbor in neighbors: for neighbor in result.neighbors:
match = _IP_NEIGH_REGEX.search(neighbor.decode('utf-8')) match = _IP_NEIGH_REGEX.search(neighbor.decode('utf-8'))
if not match: if not match:
_LOGGER.warning("Could not parse neighbor row: %s", neighbor) _LOGGER.warning("Could not parse neighbor row: %s", neighbor)