Skip to content

Commit

Permalink
Merge pull request #5 from mzdrale/dev
Browse files Browse the repository at this point in the history
Autodetect Ubee model
  • Loading branch information
StevenLooman authored Mar 26, 2019
2 parents 0dc8c4c + 65f0e64 commit 3912359
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 32 deletions.
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

0 comments on commit 3912359

Please sign in to comment.