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

Autodetect Ubee model #5

Merged
merged 9 commits into from
Mar 26, 2019
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
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
*.pyc
*.egg*
.cache
.coverage
.idea
.pytest_cache
.tox
.vscode/
.DS_Store/
Test/
build/
dist/
pyubee.egg-info/
__pycache__

3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# PyUbee CHANGELOG
This file is used to list changes made in each version of the PyUbee.

## 0.4 (March 26 2019)
* Detect Ubee model automatically ([@mzdrale](https://github.com/mzdrale) - [#5](https://github.com/mzdrale/pyubee/pull/5))

## 0.3 (March 12 2019)
* Add pyubee command line interface ([@StevenLooman](https://github.com/StevenLooman) - [#3](https://github.com/mzdrale/pyubee/pull/3))
* Add support for EVW3200-Wifi model ([@StevenLooman](https://github.com/StevenLooman) - [#3](https://github.com/mzdrale/pyubee/pull/3))
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ from pyubee import Ubee
ubee = Ubee(
host='192.168.1.1',
username='admin',
password='somepassword',
model='EVW32C-0N'
password='somepassword'
)

if not ubee.session_active():
Expand All @@ -34,7 +33,7 @@ ubee.logout()
CLI
---

A simple command line interface is available to query the router. The cli takes `host`, `username`, and `password` as mandatory arguments. The optional argument model can be used to specify the model of your routers (defaults to `EVW32C-0N`.)
A simple command line interface is available to query the router. The cli takes `host`, `username`, and `password` as mandatory arguments. The optional argument model can be used to specify the model of your routers. If model is not specified, this tool will try to detect it automatically.
```
$ pyubee --help
usage: pyubee [-h] [--model MODEL] host username password
Expand All @@ -50,9 +49,9 @@ optional arguments:
-h, --help show this help message and exit
--model MODEL Model, supported models: EVW32C-0N, EVW3200-Wifi

$ pyubee 192.168.1.1 admin somepassword --model EVW3200-Wifi
AA:BB:CC:DD:EE:FF 192.168.001.010
FF:EE:DD:CC:BB:AA 192.168.001.011
$ pyubee 192.168.1.1 admin somepassword
AA:BB:CC:DD:EE:FF 192.168.1.10
FF:EE:DD:CC:BB:AA 192.168.1.11
```

Notice
Expand Down Expand Up @@ -96,3 +95,4 @@ This library was written for and tested with:

* Ubee EVW32C-0N
* Ubee EVW3200-Wifi
* Ambit EVW320B
75 changes: 54 additions & 21 deletions pyubee/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

import logging
import re
import sys

import requests
from requests.exceptions import RequestException


_LOGGER = logging.getLogger(__name__)

MODEL_REGEX = re.compile(r'<modelName>(.*)</modelName>')

MODELS = {
'EVW32C-0N': {
'url_session_active': '/UbeeSysInfo.asp',
Expand All @@ -20,7 +21,7 @@
'regex_login': re.compile(r'<title>Residential Gateway Login</title>'),
'regex_wifi_devices': re.compile(
r'<tr bgcolor=#[0-9a-fA-F]+>'
r'<td>([0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:' # mac address
r'<td>([0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:' # mac address
r'[0-9a-fA-F]{2}:[0-9a-fA-F]{2})</td>' # mac address, cont'd
r'<td>\d+</td>' # age
r'<td>.+</td>' # rssi
Expand All @@ -37,7 +38,7 @@
r'<td>(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})</td>' # ip address
),
},
'EVW3200-Wifi': {
'EVW320B': {
'url_session_active': '/BasicStatus.asp',
'url_login': '/goform/loginMR3',
'url_logout': '/logout.asp',
Expand All @@ -46,7 +47,7 @@
'regex_login': re.compile(r'<title>Residential Gateway Login</title>'),
'regex_wifi_devices': re.compile(
r'<tr bgcolor=#[0-9a-fA-F]+>'
r'<td>([0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:' # mac address
r'<td>([0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:' # mac address
r'[0-9a-fA-F]{2}:[0-9a-fA-F]{2})</td>' # mac address, cont'd
r'<td>\d+</td>' # age
r'<td>.+</td>' # rssi
Expand All @@ -70,27 +71,58 @@
class Ubee:
"""Represents a session to a Ubee Router."""

def __init__(self, host=None, username=None, password=None, model='EVW32C-0N'):
def __init__(self, host=None, username=None, password=None, model='detect'):
"""Initialize a Ubee session."""
self.host = host
self.username = username
self.password = password

if model == 'detect':
model = self.detect_model()

if model not in MODELS:
raise LookupError('Unknown model')

self.host = host
self.username = username
self.password = password
self.model = model
self._model_info = MODELS[model]

@property
def _base_url(self):
"""Form base url."""
return 'http://{}'.format(self.host)

def _get(self, url):
"""Do a HTTP GET."""
# pylint: disable=no-self-use
return requests.get(url, timeout=4)

def _post(self, url, data):
"""Do a HTTP POST."""
# pylint: disable=no-self-use
return requests.post(url, data=data, timeout=4)

def detect_model(self):
"""Autodetect Ubee model."""
url = self._base_url + "/RootDevice.xml"
try:
response = self._get(url)
except RequestException as ex:
_LOGGER.error("Connection to the router failed: %s", ex)
return "Unknown"

data = response.text
entries = MODEL_REGEX.findall(data)

if entries:
return entries[1]

return "Unknown"

def session_active(self):
"""Check if session is active."""
url = self._base_url + self._model_info['url_session_active']
try:
response = requests.get(url, timeout=4)
response = self._get(url)
except RequestException as ex:
_LOGGER.error("Connection to the router failed: %s", ex)
return False
Expand All @@ -110,7 +142,7 @@ def login(self):
'loginPassword': self.password
}
try:
response = requests.post(url, data=payload, timeout=4)
response = self._post(url, payload)
except RequestException as ex:
_LOGGER.error("Connection to the router failed: %s", ex)
return False
Expand All @@ -127,10 +159,10 @@ def login(self):
return False

def logout(self):
"""Logout from Admin interface"""
"""Logout from Admin interface."""
url = self._base_url + self._model_info['url_logout']
try:
response = requests.get(url, timeout=4)
response = self._get(url)
except RequestException as ex:
_LOGGER.error("Connection to the router failed: %s", ex)
return False
Expand All @@ -141,7 +173,7 @@ def logout(self):
return False

def get_connected_devices(self):
"""Get list of connected devices"""
"""Get list of connected devices."""
lan_devices = self.get_connected_devices_lan()
_LOGGER.debug('LAN devices: %s', lan_devices)
wifi_devices = self.get_connected_devices_wifi()
Expand All @@ -150,17 +182,11 @@ def get_connected_devices(self):
devices.update(wifi_devices)
return devices

def _format_mac_address(self, address):
"""Format a given address to a default format."""
# remove all ':' and '-'
bare = address.upper().replace(':', '').replace('-', '')
return ':'.join(bare[i:i + 2] for i in range(0, 12, 2))

def get_connected_devices_lan(self):
"""Get list of connected devices via ethernet."""
url = self._base_url + self._model_info['url_connected_devices_lan']
try:
response = requests.get(url, timeout=4)
response = self._get(url)
except RequestException as ex:
_LOGGER.error("Connection to the router failed: %s", ex)
return []
Expand All @@ -176,7 +202,7 @@ def get_connected_devices_wifi(self):
"""Get list of connected devices via wifi."""
url = self._base_url + self._model_info['url_connected_devices_wifi']
try:
response = requests.get(url, timeout=4)
response = self._get(url)
except RequestException as ex:
_LOGGER.error("Connection to the router failed: %s", ex)
return []
Expand All @@ -187,3 +213,10 @@ def get_connected_devices_wifi(self):
self._format_mac_address(address): hostname
for address, hostname in entries
}

def _format_mac_address(self, address):
"""Format a given address to a default format."""
# pylint: disable=no-self-use
# remove all ':' and '-'
bare = address.upper().replace(':', '').replace('-', '')
return ':'.join(bare[i:i + 2] for i in range(0, 12, 2))
11 changes: 8 additions & 3 deletions pyubee/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def main():
parser.add_argument('host', help='Host')
parser.add_argument('username', help='Username')
parser.add_argument('password', help='Password')
parser.add_argument('--model', default="EVW32C-0N",
parser.add_argument('-m', '--model', default="detect",
help='Model, supported models: ' + ', '.join(SUPPORTED_MODELS))
args = parser.parse_args()

Expand All @@ -27,8 +27,13 @@ def main():
sys.exit(1)

devices = ubee.get_connected_devices()
for device in devices:
print("%s\t%s" % (device, devices[device]))

if devices:
print("Connected devices:")
for device in devices:
print("%s\t%s" % (device, devices[device]))
else:
print("No connected devices found")


if __name__ == '__main__':
Expand Down
5 changes: 5 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[metadata]
license_file = LICENSE

[flake8]
max-line-length = 119
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setuptools.setup(
name="pyubee",
version="0.3",
version="0.4.dev2",
author="Miroslav Zdrale",
author_email="mzdrale@gmail.com",
description="Simple library for getting stats from Ubee routers.",
Expand Down
22 changes: 22 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[tox]
envlist = py35, py36, py37, flake8, pylint, pydocstyle

[testenv:flake8]
basepython = python3
ignore_errors = True
deps = flake8
commands = flake8 pyubee

[testenv:pylint]
basepython = python3
ignore_errors = True
deps =
pylint
requests
commands = pylint pyubee

[testenv:pydocstyle]
basepython = python3
ignore_errors = True
deps = pydocstyle
commands = pydocstyle pyubee