Closed halkeye closed 6 years ago
Then we are 2 confused people at the moment :/ I'm not 100% sure if it's the correct version, but it should be.
Yes its 1.1.2 and that one contains the updated code... ? Will do more tests
aioasuswrt 1.1.2```
Thats whats installed in the docker image
admin@RT-N66U-8038:/tmp/home/root# arp -n
? (172.16.10.142) at 00:18:DD:04:E2:FA [ether] on br0
? (172.16.10.1) at 00:08:A2:0D:54:B8 [ether] on br0
? (172.16.10.125) at 60:01:94:41:C4:21 [ether] on br0
? (172.16.10.61) at 00:09:B0:A1:51:3C [ether] on br0
? (172.16.10.2) at 00:25:90:12:2D:90 [ether] on br0
admin@RT-N66U-8038:/tmp/home/root# arp -n | hexdump
0000000 203f 3128 3237 312e 2e36 3031 312e 3234
0000010 2029 7461 3020 3a30 3831 443a 3a44 3430
0000020 453a 3a32 4146 5b20 7465 6568 5d72 2020
0000030 6e6f 6220 3072 3f0a 2820 3731 2e32 3631
0000040 312e 2e30 2931 6120 2074 3030 303a 3a38
0000050 3241 303a 3a44 3435 423a 2038 655b 6874
0000060 7265 205d 6f20 206e 7262 0a30 203f 3128
0000070 3237 312e 2e36 3031 312e 3532 2029 7461
0000080 3620 3a30 3130 393a 3a34 3134 433a 3a34
0000090 3132 5b20 7465 6568 5d72 2020 6e6f 6220
00000a0 3072 3f0a 2820 3731 2e32 3631 312e 2e30
00000b0 3136 2029 7461 3020 3a30 3930 423a 3a30
00000c0 3141 353a 3a31 4333 5b20 7465 6568 5d72
00000d0 2020 6e6f 6220 3072 3f0a 2820 3731 2e32
00000e0 3631 312e 2e30 2932 6120 2074 3030 323a
00000f0 3a35 3039 313a 3a32 4432 393a 2030 655b
0000100 6874 7265 205d 6f20 206e 7262 0a30
000010e
This is so strange!!! could it be encoding?
How can I test that?
On Sun., Nov. 11, 2018, 10:45 p.m. kennedyshead <notifications@github.com wrote:
This is so strange!!! could it be encoding?
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/kennedyshead/aioasuswrt/issues/8#issuecomment-437773867, or mute the thread https://github.com/notifications/unsubscribe-auth/AAGuBziU5HVZF1HaN6pNtkApovGEAunaks5uuRj2gaJpZM4YYwIG .
Try this:
async def _parse_lines(lines, regex):
"""Parse the lines using the given regular expression.
If a line can't be parsed it is logged and skipped in the output.
"""
results = []
if inspect.iscoroutinefunction(lines):
lines = await lines
for line in lines:
if line:
match = regex.search(line)
if not match:
_LOGGER.debug("Could not parse row: %s", line)
_LOGGER.debug(type(line))
_LOGGER.debug(chardet.detect(line)['encoding'])
continue
results.append(match.groupdict())
return results
And oh import chardet ;)
2018-11-11 23:19:44 DEBUG (MainThread) [aioasuswrt.asuswrt] Could not parse row:
2018-11-11 23:19:44 DEBUG (MainThread) [aioasuswrt.asuswrt] <class 'str'>
2018-11-11 23:19:44 ERROR (MainThread) [homeassistant.components.device_tracker] Error setting up platform asuswrt
Traceback (most recent call last):
File "/usr/src/app/homeassistant/components/device_tracker/__init__.py", line 174, in async_setup_platform
hass, {DOMAIN: p_config})
File "/usr/src/app/homeassistant/components/device_tracker/asuswrt.py", line 46, in async_get_scanner
await scanner.async_connect()
File "/usr/src/app/homeassistant/components/device_tracker/asuswrt.py", line 71, in async_connect
data = await self.connection.async_get_connected_devices()
File "/usr/local/lib/python3.6/site-packages/aioasuswrt/asuswrt.py", line 160, in async_get_connected_devices
devices.update(await self.async_get_wl())
File "/usr/local/lib/python3.6/site-packages/aioasuswrt/asuswrt.py", line 99, in async_get_wl
result = _parse_lines(lines, _WL_REGEX)
File "/usr/local/lib/python3.6/site-packages/aioasuswrt/asuswrt.py", line 66, in _parse_lines
_LOGGER.debug(chardet.detect(line)['encoding'])
File "/usr/local/lib/python3.6/site-packages/chardet/__init__.py", line 34, in detect
'{0}'.format(type(byte_str)))
TypeError: Expected object of type bytes or bytearray, got: <class 'str'>
changed it to:
def _parse_lines(lines, regex):
"""Parse the lines using the given regular expression.
If a line can't be parsed it is logged and skipped in the output.
"""
results = []
for line in lines:
match = regex.search(line)
if not match:
_LOGGER.debug("Could not parse row: %s", line)
import chardet
_LOGGER.debug(type(line))
if isinstance(line, str):
line = str.encode(line)
_LOGGER.debug(chardet.detect(line)['encoding'])
continue
results.append(match.groupdict())
return results
got
2018-11-11 23:23:49 DEBUG (MainThread) [aioasuswrt.asuswrt] Could not parse row: ? (172.16.10.142) at 00:18:DD:04:E2:FA [ether] on br0
2018-11-11 23:23:49 DEBUG (MainThread) [aioasuswrt.asuswrt] <class 'str'>
2018-11-11 23:23:49 DEBUG (MainThread) [aioasuswrt.asuswrt] ascii
2018-11-11 23:23:49 DEBUG (MainThread) [aioasuswrt.asuswrt] Could not parse row: ? (172.16.10.1) at 00:08:A2:0D:54:B8 [ether] on br0
2018-11-11 23:23:49 DEBUG (MainThread) [aioasuswrt.asuswrt] <class 'str'>
2018-11-11 23:23:49 DEBUG (MainThread) [aioasuswrt.asuswrt] ascii
2018-11-11 23:23:49 DEBUG (MainThread) [aioasuswrt.asuswrt] Could not parse row: ? (172.16.10.125) at 60:01:94:41:C4:21 [ether] on br0
2018-11-11 23:23:49 DEBUG (MainThread) [aioasuswrt.asuswrt] <class 'str'>
2018-11-11 23:23:49 DEBUG (MainThread) [aioasuswrt.asuswrt] ascii
2018-11-11 23:23:49 DEBUG (MainThread) [aioasuswrt.asuswrt] Could not parse row: ? (172.16.10.61) at 00:09:B0:A1:51:3C [ether] on br0
2018-11-11 23:23:49 DEBUG (MainThread) [aioasuswrt.asuswrt] <class 'str'>
2018-11-11 23:23:49 DEBUG (MainThread) [aioasuswrt.asuswrt] ascii
2018-11-11 23:23:49 DEBUG (MainThread) [aioasuswrt.asuswrt] Could not parse row: ? (172.16.10.2) at 00:25:90:12:2D:90 [ether] on br0
2018-11-11 23:23:49 DEBUG (MainThread) [aioasuswrt.asuswrt] <class 'str'>
2018-11-11 23:23:49 DEBUG (MainThread) [aioasuswrt.asuswrt] ascii
2018-11-11 23:23:49 DEBUG (MainThread) [aioasuswrt.asuswrt] Could not parse row:
2018-11-11 23:23:49 DEBUG (MainThread) [aioasuswrt.asuswrt] <class 'str'>
Test this:
"""Moddule for Asuswrt."""
import inspect
import logging
import math
import re
from collections import namedtuple
from datetime import datetime
from aioasuswrt.connection import SshConnection, TelnetConnection
from aioasuswrt.helpers import convert_size
_LOGGER = logging.getLogger(__name__)
CHANGE_TIME_CACHE_DEFAULT = 5 # Default 60s
_LEASES_CMD = 'cat /var/lib/misc/dnsmasq.leases'
_LEASES_REGEX = re.compile(
r'\w+\s' +
r'(?P<mac>(([0-9a-fA-F]{2}[:-]){5}([0-9a-fA-F]{2})))\s' +
r'(?P<ip>([0-9]{1,3}[\.]){3}[0-9]{1,3})\s' +
r'(?P<host>([^\s]+))')
# Command to get both 5GHz and 2.4GHz clients
_WL_CMD = 'for dev in `nvram get wl_ifnames`; do wl -i $dev assoclist; done'
_WL_REGEX = re.compile(
r'\w+\s' +
r'(?P<mac>(([0-9A-F]{2}[:-]){5}([0-9A-F]{2})))')
_IP_NEIGH_CMD = 'ip neigh'
_IP_NEIGH_REGEX = re.compile(
r'(?P<ip>([0-9]{1,3}[\.]){3}[0-9]{1,3}|'
r'([0-9a-fA-F]{1,4}:){1,7}[0-9a-fA-F]{0,4}(:[0-9a-fA-F]{1,4}){1,7})\s'
r'\w+\s'
r'\w+\s'
r'(\w+\s(?P<mac>(([0-9a-fA-F]{2}[:-]){5}([0-9a-fA-F]{2}))))?\s'
r'\s?(router)?'
r'\s?(nud)?'
r'(?P<status>(\w+))')
_ARP_CMD = 'arp -n'
_ARP_REGEX = re.compile(
r'.+\s' +
r'\((?P<ip>([0-9]{1,3}[\.]){3}[0-9]{1,3})\)\s' +
r'.+\s' +
r'(?P<mac>(([0-9a-fA-F]{2}[:-]){5}([0-9a-fA-F]{2})))' +
r'\s' +
r'.*')
_IFCONFIG_CMD = 'ifconfig eth0 |grep bytes'
_IFCONFIG_REGEX = re.compile(
r'(?P<data>[\d]{4,})')
_IP_LINK_CMD = "ip -rc 1024 -s link"
Device = namedtuple('Device', ['mac', 'ip', 'name'])
async def _parse_lines(lines, regex):
"""Parse the lines using the given regular expression.
If a line can't be parsed it is logged and skipped in the output.
"""
results = []
if inspect.iscoroutinefunction(lines):
lines = await lines
for line in lines:
if line:
match = regex.search(line)
if not match:
_LOGGER.debug("Could not parse row: %s", line)
_LOGGER.debug(type(line))
continue
results.append(match.groupdict())
return results
class AsusWrt:
"""This is the interface class."""
def __init__(self, host, port, use_telnet=False, username=None,
password=None, ssh_key=None, mode='router', require_ip=False,
time_cache=CHANGE_TIME_CACHE_DEFAULT):
"""Init function."""
self.require_ip = require_ip
self.mode = mode
self._rx_latest = None
self._tx_latest = None
self._latest_transfer_check = None
self._cache_time = time_cache
self._trans_cache_timer = None
self._transfer_rates_cache = None
if use_telnet:
self.connection = TelnetConnection(
host, port, username, password)
else:
self.connection = SshConnection(
host, port, username, password, ssh_key)
async def async_get_wl(self):
lines = await self.connection.async_run_command(_WL_CMD)
if not lines:
return {}
result = await _parse_lines(lines, _WL_REGEX)
devices = {}
for device in result:
mac = device['mac'].upper()
devices[mac] = Device(mac, None, None)
return devices
async def async_get_leases(self, cur_devices):
lines = await self.connection.async_run_command(_LEASES_CMD)
if not lines:
return {}
lines = [line for line in lines if not line.startswith('duid ')]
result = await _parse_lines(lines, _LEASES_REGEX)
devices = {}
for device in result:
# For leases where the client doesn't set a hostname, ensure it
# is blank and not '*', which breaks entity_id down the line.
host = device['host']
if host == '*':
host = ''
mac = device['mac'].upper()
if mac in cur_devices:
devices[mac] = Device(mac, device['ip'], host)
return devices
async def async_get_neigh(self, cur_devices):
lines = await self.connection.async_run_command(_IP_NEIGH_CMD)
if not lines:
return {}
result = _parse_lines(lines, _IP_NEIGH_REGEX)
devices = {}
for device in await result:
status = device['status']
if status is None or status.upper() != 'REACHABLE':
continue
if device['mac'] is not None:
mac = device['mac'].upper()
old_device = cur_devices.get(mac)
old_ip = old_device.ip if old_device else None
devices[mac] = Device(mac, device.get('ip', old_ip), None)
return devices
async def async_get_arp(self):
lines = await self.connection.async_run_command(_ARP_CMD)
if not lines:
return {}
result = await _parse_lines(lines, _ARP_REGEX)
devices = {}
for device in result:
if device['mac'] is not None:
mac = device['mac'].upper()
devices[mac] = Device(mac, device['ip'], None)
return devices
async def async_get_connected_devices(self):
"""Retrieve data from ASUSWRT.
Calls various commands on the router and returns the superset of all
responses. Some commands will not work on some routers.
"""
devices = {}
dev = await self.async_get_wl()
devices.update(dev)
dev = await self.async_get_arp()
devices.update(dev)
dev = await self.async_get_neigh(devices)
devices.update(dev)
if not self.mode == 'ap':
dev = await self.async_get_leases(devices)
devices.update(dev)
ret_devices = {}
for key in devices:
if not self.require_ip or devices[key].ip is not None:
ret_devices[key] = devices[key]
return ret_devices
async def async_get_packets_total(self, use_cache=True):
"""Retrieve total packets from ASUSWRT."""
now = datetime.utcnow()
if use_cache and self._trans_cache_timer and self._cache_time > \
(now - self._trans_cache_timer).total_seconds():
return self._transfer_rates_cache
data = await self.connection.async_run_command(_IP_LINK_CMD)
_LOGGER.info(data)
i = 0
rx = 0
tx = 0
for line in data:
if 'eth0' in line:
rx = data[i+3].split(' ')[4]
tx = data[i+5].split(' ')[4]
break
i += 1
return int(rx), int(tx)
async def async_get_rx(self, use_cache=True):
"""Get current RX total given in bytes."""
data = await self.async_get_packets_total(use_cache)
return data[0]
async def async_get_tx(self, use_cache=True):
"""Get current RX total given in bytes."""
data = await self.async_get_packets_total(use_cache)
return data[1]
async def async_get_current_transfer_rates(self, use_cache=True):
"""Gets current transfer rates calculated in per second in bytes."""
now = datetime.utcnow()
data = await self.async_get_packets_total(use_cache)
if self._rx_latest is None or self._tx_latest is None:
self._latest_transfer_check = now
self._rx_latest = data[0]
self._tx_latest = data[1]
return
time_diff = now - self._latest_transfer_check
if time_diff.total_seconds() < 30:
return (
math.ceil(
self._rx_latest / time_diff.total_seconds()
) if self._rx_latest > 0 else 0,
math.ceil(
self._tx_latest / time_diff.total_seconds()
) if self._tx_latest > 0 else 0)
self._latest_transfer_check = now
rx = data[0] - self._rx_latest
tx = data[1] - self._tx_latest
self._rx_latest = data[0]
self._tx_latest = data[1]
return (
math.ceil(rx / time_diff.total_seconds()) if rx > 0 else 0,
math.ceil(tx / time_diff.total_seconds()) if tx > 0 else 0)
async def async_current_transfer_human_readable(
self, use_cache=True):
"""Gets current transfer rates in a human readable format."""
rx, tx = await self.async_get_current_transfer_rates(use_cache)
return "%s/s" % convert_size(rx), "%s/s" % convert_size(tx)
@property
def is_connected(self):
return self.connection.is_connected
That is it... just tested it... I'm not really sure why everything was broken because it actually works for me without the uc match... strange
so just tested 1.1.10 and things seem to be coming in now. So woo!
:+1:
I tried adding the row to the ARP_DATA tests, but its not failing, so I'm confused whats going on.
This is Home Assistant 0.82.0 which should have the new version in it.