Add device tracker support for EE Brightbox 2 router (#19611)

* Added device tracker support for EE Brightbox 2

* removed timeago dependency

* get scanner checks and improved tests

* fixed lint issues

* removed redundant timeago from test requirements

* fixed variable naming in test

* removed unecessary blank line
This commit is contained in:
krygal 2019-01-22 08:16:35 +00:00 committed by Martin Hjelmare
parent 5a30b0507d
commit a8ef7a2774
5 changed files with 236 additions and 0 deletions

View file

@ -0,0 +1,107 @@
"""
Support for EE Brightbox router.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.ee_brightbox/
"""
import logging
import voluptuous as vol
from homeassistant.components.device_tracker import (
DOMAIN, PLATFORM_SCHEMA, DeviceScanner)
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['eebrightbox==0.0.4']
_LOGGER = logging.getLogger(__name__)
CONF_VERSION = 'version'
CONF_DEFAULT_IP = '192.168.1.1'
CONF_DEFAULT_USERNAME = 'admin'
CONF_DEFAULT_VERSION = 2
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_VERSION, default=CONF_DEFAULT_VERSION): cv.positive_int,
vol.Required(CONF_HOST, default=CONF_DEFAULT_IP): cv.string,
vol.Required(CONF_USERNAME, default=CONF_DEFAULT_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
})
def get_scanner(hass, config):
"""Return a router scanner instance."""
scanner = EEBrightBoxScanner(config[DOMAIN])
return scanner if scanner.check_config() else None
class EEBrightBoxScanner(DeviceScanner):
"""Scan EE Brightbox router."""
def __init__(self, config):
"""Initialise the scanner."""
self.config = config
self.devices = {}
def check_config(self):
"""Check if provided configuration and credentials are correct."""
from eebrightbox import EEBrightBox, EEBrightBoxException
try:
with EEBrightBox(self.config) as ee_brightbox:
return bool(ee_brightbox.get_devices())
except EEBrightBoxException:
_LOGGER.exception("Failed to connect to the router")
return False
def scan_devices(self):
"""Scan for devices."""
from eebrightbox import EEBrightBox
with EEBrightBox(self.config) as ee_brightbox:
self.devices = {d['mac']: d for d in ee_brightbox.get_devices()}
macs = [d['mac'] for d in self.devices.values() if d['activity_ip']]
_LOGGER.debug('Scan devices %s', macs)
return macs
def get_device_name(self, device):
"""Get the name of a device from hostname."""
if device in self.devices:
return self.devices[device]['hostname'] or None
return None
def get_extra_attributes(self, device):
"""
Get the extra attributes of a device.
Extra attributes include:
- ip
- mac
- port - ethX or wifiX
- last_active
"""
port_map = {
'wl1': 'wifi5Ghz',
'wl0': 'wifi2.4Ghz',
'eth0': 'eth0',
'eth1': 'eth1',
'eth2': 'eth2',
'eth3': 'eth3',
}
if device in self.devices:
return {
'ip': self.devices[device]['ip'],
'mac': self.devices[device]['mac'],
'port': port_map[self.devices[device]['port']],
'last_active': self.devices[device]['time_last_active'],
}
return {}

View file

@ -342,6 +342,9 @@ dweepy==0.3.0
# homeassistant.components.edp_redy
edp_redy==0.0.3
# homeassistant.components.device_tracker.ee_brightbox
eebrightbox==0.0.4
# homeassistant.components.media_player.horizon
einder==0.3.1

View file

@ -61,6 +61,9 @@ defusedxml==0.5.0
# homeassistant.components.sensor.dsmr
dsmr_parser==0.12
# homeassistant.components.device_tracker.ee_brightbox
eebrightbox==0.0.4
# homeassistant.components.emulated_roku
emulated_roku==0.1.7

View file

@ -46,6 +46,7 @@ TEST_REQUIREMENTS = (
'coinmarketcap',
'defusedxml',
'dsmr_parser',
'eebrightbox',
'emulated_roku',
'enturclient',
'ephem',

View file

@ -0,0 +1,122 @@
"""Tests for the EE BrightBox device scanner."""
from datetime import datetime
from asynctest import patch
import pytest
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import (
CONF_PASSWORD, CONF_PLATFORM)
from homeassistant.setup import async_setup_component
def _configure_mock_get_devices(eebrightbox_mock):
eebrightbox_instance = eebrightbox_mock.return_value
eebrightbox_instance.__enter__.return_value = eebrightbox_instance
eebrightbox_instance.get_devices.return_value = [
{
'mac': 'AA:BB:CC:DD:EE:FF',
'ip': '192.168.1.10',
'hostname': 'hostnameAA',
'activity_ip': True,
'port': 'eth0',
'time_last_active': datetime(2019, 1, 20, 16, 4, 0),
},
{
'mac': '11:22:33:44:55:66',
'hostname': 'hostname11',
'ip': '192.168.1.11',
'activity_ip': True,
'port': 'wl0',
'time_last_active': datetime(2019, 1, 20, 11, 9, 0),
},
{
'mac': 'FF:FF:FF:FF:FF:FF',
'hostname': 'hostnameFF',
'ip': '192.168.1.12',
'activity_ip': False,
'port': 'wl1',
'time_last_active': datetime(2019, 1, 15, 16, 9, 0),
}
]
def _configure_mock_failed_config_check(eebrightbox_mock):
from eebrightbox import EEBrightBoxException
eebrightbox_instance = eebrightbox_mock.return_value
eebrightbox_instance.__enter__.side_effect = EEBrightBoxException(
"Failed to connect to the router")
@pytest.fixture(autouse=True)
def mock_dev_track(mock_device_tracker_conf):
"""Mock device tracker config loading."""
pass
@patch('eebrightbox.EEBrightBox')
async def test_missing_credentials(eebrightbox_mock, hass):
"""Test missing credentials."""
_configure_mock_get_devices(eebrightbox_mock)
result = await async_setup_component(hass, DOMAIN, {
DOMAIN: {
CONF_PLATFORM: 'ee_brightbox',
}
})
assert result
await hass.async_block_till_done()
assert hass.states.get('device_tracker.hostnameaa') is None
assert hass.states.get('device_tracker.hostname11') is None
assert hass.states.get('device_tracker.hostnameff') is None
@patch('eebrightbox.EEBrightBox')
async def test_invalid_credentials(eebrightbox_mock, hass):
"""Test invalid credentials."""
_configure_mock_failed_config_check(eebrightbox_mock)
result = await async_setup_component(hass, DOMAIN, {
DOMAIN: {
CONF_PLATFORM: 'ee_brightbox',
CONF_PASSWORD: 'test_password',
}
})
assert result
await hass.async_block_till_done()
assert hass.states.get('device_tracker.hostnameaa') is None
assert hass.states.get('device_tracker.hostname11') is None
assert hass.states.get('device_tracker.hostnameff') is None
@patch('eebrightbox.EEBrightBox')
async def test_get_devices(eebrightbox_mock, hass):
"""Test valid configuration."""
_configure_mock_get_devices(eebrightbox_mock)
result = await async_setup_component(hass, DOMAIN, {
DOMAIN: {
CONF_PLATFORM: 'ee_brightbox',
CONF_PASSWORD: 'test_password',
}
})
assert result
await hass.async_block_till_done()
assert hass.states.get('device_tracker.hostnameaa') is not None
assert hass.states.get('device_tracker.hostname11') is not None
assert hass.states.get('device_tracker.hostnameff') is None
state = hass.states.get('device_tracker.hostnameaa')
assert state.attributes['mac'] == 'AA:BB:CC:DD:EE:FF'
assert state.attributes['ip'] == '192.168.1.10'
assert state.attributes['port'] == 'eth0'
assert state.attributes['last_active'] == datetime(2019, 1, 20, 16, 4, 0)