Fix bt_home_hub_5 device tracker (#15096)

* Fix bt_home_hub_5 device tracker

Updated BT Home Hub 5 device tracker component to get it working again. The old parsing method of the DNS table has been broken for a while causing the component to fail to get connected devices. A new parsing method has been implemened and fixes all previous issues.

* Moved part of code to a published PyPi library

* Fixed Violations

* Fixed bugs in device tracker

* Moved API Specific Code to PyPi Repository

* Updated to fit requested changes, removed test as it is no longer valid and updated requirement_all.txt

* Update to fit style requirements and remove redundant code

* Removed Unnecessary Comment
This commit is contained in:
ahobsonsayers
2018-08-06 06:38:02 +01:00
committed by Martin Hjelmare
parent ac4674fdb0
commit e4b2ae29bd
3 changed files with 21 additions and 120 deletions

View File

@ -5,24 +5,22 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.bt_home_hub_5/
"""
import logging
import re
import xml.etree.ElementTree as ET
import json
from urllib.parse import unquote
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.components.device_tracker import (DOMAIN, PLATFORM_SCHEMA,
DeviceScanner)
from homeassistant.const import CONF_HOST
REQUIREMENTS = ['bthomehub5-devicelist==0.1.1']
_LOGGER = logging.getLogger(__name__)
_MAC_REGEX = re.compile(r'(([0-9A-Fa-f]{1,2}\:){5}[0-9A-Fa-f]{1,2})')
CONF_DEFAULT_IP = '192.168.1.254'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string
vol.Optional(CONF_HOST, default=CONF_DEFAULT_IP): cv.string,
})
@ -38,18 +36,19 @@ class BTHomeHub5DeviceScanner(DeviceScanner):
def __init__(self, config):
"""Initialise the scanner."""
import bthomehub5_devicelist
_LOGGER.info("Initialising BT Home Hub 5")
self.host = config.get(CONF_HOST, '192.168.1.254')
self.host = config[CONF_HOST]
self.last_results = {}
self.url = 'http://{}/nonAuth/home_status.xml'.format(self.host)
# Test the router is accessible
data = _get_homehub_data(self.url)
data = bthomehub5_devicelist.get_devicelist(self.host)
self.success_init = data is not None
def scan_devices(self):
"""Scan for new devices and return a list with found device IDs."""
self._update_info()
self.update_info()
return (device for device in self.last_results)
@ -57,71 +56,23 @@ class BTHomeHub5DeviceScanner(DeviceScanner):
"""Return the name of the given device or None if we don't know."""
# If not initialised and not already scanned and not found.
if device not in self.last_results:
self._update_info()
self.update_info()
if not self.last_results:
return None
return self.last_results.get(device)
def _update_info(self):
"""Ensure the information from the BT Home Hub 5 is up to date.
Return boolean if scanning successful.
"""
if not self.success_init:
return False
def update_info(self):
"""Ensure the information from the BT Home Hub 5 is up to date."""
import bthomehub5_devicelist
_LOGGER.info("Scanning")
data = _get_homehub_data(self.url)
data = bthomehub5_devicelist.get_devicelist(self.host)
if not data:
_LOGGER.warning("Error scanning devices")
return False
return
self.last_results = data
return True
def _get_homehub_data(url):
"""Retrieve data from BT Home Hub 5 and return parsed result."""
try:
response = requests.get(url, timeout=5)
except requests.exceptions.Timeout:
_LOGGER.exception("Connection to the router timed out")
return
if response.status_code == 200:
return _parse_homehub_response(response.text)
_LOGGER.error("Invalid response from Home Hub: %s", response)
def _parse_homehub_response(data_str):
"""Parse the BT Home Hub 5 data format."""
root = ET.fromstring(data_str)
dirty_json = root.find('known_device_list').get('value')
# Normalise the JavaScript data to JSON.
clean_json = unquote(dirty_json.replace('\'', '\"')
.replace('{', '{\"')
.replace(':\"', '\":\"')
.replace('\",', '\",\"'))
known_devices = [x for x in json.loads(clean_json) if x]
devices = {}
for device in known_devices:
name = device.get('name')
mac = device.get('mac')
if _MAC_REGEX.match(mac) or ',' in mac:
for mac_addr in mac.split(','):
if _MAC_REGEX.match(mac_addr):
devices[mac_addr] = name
else:
devices[mac] = name
return devices

View File

@ -208,6 +208,9 @@ brunt==0.1.2
# homeassistant.components.device_tracker.bluetooth_tracker
bt_proximity==0.1.2
# homeassistant.components.device_tracker.bt_home_hub_5
bthomehub5-devicelist==0.1.1
# homeassistant.components.sensor.buienradar
# homeassistant.components.weather.buienradar
buienradar==0.91

View File

@ -1,53 +0,0 @@
"""The tests for the BT Home Hub 5 device tracker platform."""
import unittest
from unittest.mock import patch
from homeassistant.components.device_tracker import bt_home_hub_5
from homeassistant.const import CONF_HOST
patch_file = 'homeassistant.components.device_tracker.bt_home_hub_5'
def _get_homehub_data(url):
"""Return mock homehub data."""
return '''
[
{
"mac": "AA:BB:CC:DD:EE:FF,
"hostname": "hostname",
"ip": "192.168.1.43",
"ipv6": "",
"name": "hostname",
"activity": "1",
"os": "Unknown",
"device": "Unknown",
"time_first_seen": "2016/06/05 11:14:45",
"time_last_active": "2016/06/06 11:33:08",
"dhcp_option": "39043T90430T9TGK0EKGE5KGE3K904390K45GK054",
"port": "wl0",
"ipv6_ll": "fe80::gd67:ghrr:fuud:4332",
"activity_ip": "1",
"activity_ipv6_ll": "0",
"activity_ipv6": "0",
"device_oui": "NA",
"device_serial": "NA",
"device_class": "NA"
}
]
'''
class TestBTHomeHub5DeviceTracker(unittest.TestCase):
"""Test BT Home Hub 5 device tracker platform."""
@patch('{}._get_homehub_data'.format(patch_file), new=_get_homehub_data)
def test_config_minimal(self):
"""Test the setup with minimal configuration."""
config = {
'device_tracker': {
CONF_HOST: 'foo'
}
}
result = bt_home_hub_5.get_scanner(None, config)
self.assertIsNotNone(result)