diff --git a/homeassistant/components/asuswrt.py b/homeassistant/components/asuswrt.py new file mode 100644 index 00000000000..37a55238e6b --- /dev/null +++ b/homeassistant/components/asuswrt.py @@ -0,0 +1,68 @@ +""" +Support for ASUSWRT devices. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/asuswrt/ +""" +import logging + +import voluptuous as vol + +from homeassistant.const import ( + CONF_HOST, CONF_PASSWORD, CONF_USERNAME, CONF_PORT, CONF_MODE, + CONF_PROTOCOL) +from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.discovery import async_load_platform + +REQUIREMENTS = ['aioasuswrt==1.1.4'] + +_LOGGER = logging.getLogger(__name__) + +DOMAIN = "asuswrt" +DATA_ASUSWRT = DOMAIN + +CONF_PUB_KEY = 'pub_key' +CONF_SSH_KEY = 'ssh_key' +CONF_REQUIRE_IP = 'require_ip' +DEFAULT_SSH_PORT = 22 +SECRET_GROUP = 'Password or SSH Key' + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + vol.Required(CONF_HOST): cv.string, + vol.Required(CONF_USERNAME): cv.string, + vol.Optional(CONF_PROTOCOL, default='ssh'): vol.In(['ssh', 'telnet']), + vol.Optional(CONF_MODE, default='router'): vol.In(['router', 'ap']), + vol.Optional(CONF_PORT, default=DEFAULT_SSH_PORT): cv.port, + vol.Optional(CONF_REQUIRE_IP, default=True): cv.boolean, + vol.Exclusive(CONF_PASSWORD, SECRET_GROUP): cv.string, + vol.Exclusive(CONF_SSH_KEY, SECRET_GROUP): cv.isfile, + vol.Exclusive(CONF_PUB_KEY, SECRET_GROUP): cv.isfile + }), +}, extra=vol.ALLOW_EXTRA) + + +async def async_setup(hass, config): + """Set up the asuswrt component.""" + from aioasuswrt.asuswrt import AsusWrt + conf = config[DOMAIN] + + api = AsusWrt(conf[CONF_HOST], conf.get(CONF_PORT), + conf.get(CONF_PROTOCOL) == 'telnet', + conf[CONF_USERNAME], + conf.get(CONF_PASSWORD, ''), + conf.get('ssh_key', conf.get('pub_key', '')), + conf.get(CONF_MODE), conf.get(CONF_REQUIRE_IP)) + + await api.connection.async_connect() + if not api.is_connected: + _LOGGER.error("Unable to setup asuswrt component") + return False + + hass.data[DATA_ASUSWRT] = api + + hass.async_create_task(async_load_platform( + hass, 'sensor', DOMAIN, {}, config)) + hass.async_create_task(async_load_platform( + hass, 'device_tracker', DOMAIN, {}, config)) + return True diff --git a/homeassistant/components/device_tracker/asuswrt.py b/homeassistant/components/device_tracker/asuswrt.py index 561d41562de..4630c3730ca 100644 --- a/homeassistant/components/device_tracker/asuswrt.py +++ b/homeassistant/components/device_tracker/asuswrt.py @@ -6,43 +6,17 @@ https://home-assistant.io/components/device_tracker.asuswrt/ """ import logging -import voluptuous as vol +from homeassistant.components.asuswrt import DATA_ASUSWRT +from homeassistant.components.device_tracker import DeviceScanner -import homeassistant.helpers.config_validation as cv -from homeassistant.components.device_tracker import ( - DOMAIN, PLATFORM_SCHEMA, DeviceScanner) -from homeassistant.const import ( - CONF_HOST, CONF_PASSWORD, CONF_USERNAME, CONF_PORT, CONF_MODE, - CONF_PROTOCOL) - -REQUIREMENTS = ['aioasuswrt==1.1.2'] +DEPENDENCIES = ['asuswrt'] _LOGGER = logging.getLogger(__name__) -CONF_PUB_KEY = 'pub_key' -CONF_SSH_KEY = 'ssh_key' -CONF_REQUIRE_IP = 'require_ip' -DEFAULT_SSH_PORT = 22 -SECRET_GROUP = 'Password or SSH Key' - -PLATFORM_SCHEMA = vol.All( - cv.has_at_least_one_key(CONF_PASSWORD, CONF_PUB_KEY, CONF_SSH_KEY), - PLATFORM_SCHEMA.extend({ - vol.Required(CONF_HOST): cv.string, - vol.Required(CONF_USERNAME): cv.string, - vol.Optional(CONF_PROTOCOL, default='ssh'): vol.In(['ssh', 'telnet']), - vol.Optional(CONF_MODE, default='router'): vol.In(['router', 'ap']), - vol.Optional(CONF_PORT, default=DEFAULT_SSH_PORT): cv.port, - vol.Optional(CONF_REQUIRE_IP, default=True): cv.boolean, - vol.Exclusive(CONF_PASSWORD, SECRET_GROUP): cv.string, - vol.Exclusive(CONF_SSH_KEY, SECRET_GROUP): cv.isfile, - vol.Exclusive(CONF_PUB_KEY, SECRET_GROUP): cv.isfile - })) - async def async_get_scanner(hass, config): """Validate the configuration and return an ASUS-WRT scanner.""" - scanner = AsusWrtDeviceScanner(config[DOMAIN]) + scanner = AsusWrtDeviceScanner(hass.data[DATA_ASUSWRT]) await scanner.async_connect() return scanner if scanner.success_init else None @@ -51,19 +25,11 @@ class AsusWrtDeviceScanner(DeviceScanner): """This class queries a router running ASUSWRT firmware.""" # Eighth attribute needed for mode (AP mode vs router mode) - def __init__(self, config): + def __init__(self, api): """Initialize the scanner.""" - from aioasuswrt.asuswrt import AsusWrt - self.last_results = {} self.success_init = False - self.connection = AsusWrt(config[CONF_HOST], config[CONF_PORT], - config[CONF_PROTOCOL] == 'telnet', - config[CONF_USERNAME], - config.get(CONF_PASSWORD, ''), - config.get('ssh_key', - config.get('pub_key', '')), - config[CONF_MODE], config[CONF_REQUIRE_IP]) + self.connection = api async def async_connect(self): """Initialize connection to the router.""" diff --git a/homeassistant/components/sensor/asuswrt.py b/homeassistant/components/sensor/asuswrt.py new file mode 100644 index 00000000000..4ca088fb1e2 --- /dev/null +++ b/homeassistant/components/sensor/asuswrt.py @@ -0,0 +1,126 @@ +""" +Asuswrt status sensors. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.asuswrt/ +""" +import logging + +from homeassistant.helpers.entity import Entity +from homeassistant.components.asuswrt import DATA_ASUSWRT + +DEPENDENCIES = ['asuswrt'] + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_platform( + hass, config, add_entities, discovery_info=None): + """Set up the asuswrt sensors.""" + api = hass.data[DATA_ASUSWRT] + add_entities([ + AsuswrtRXSensor(api), + AsuswrtTXSensor(api), + AsuswrtTotalRXSensor(api), + AsuswrtTotalTXSensor(api) + ]) + + +class AsuswrtSensor(Entity): + """Representation of a asuswrt sensor.""" + + _name = 'generic' + + def __init__(self, api): + """Initialize the sensor.""" + self._api = api + self._state = None + self._rates = None + self._speed = None + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def state(self): + """Return the state of the sensor.""" + return self._state + + async def async_update(self): + """Fetch status from asuswrt.""" + self._rates = await self._api.async_get_packets_total() + self._speed = await self._api.async_get_current_transfer_rates() + + +class AsuswrtRXSensor(AsuswrtSensor): + """Representation of a asuswrt download speed sensor.""" + + _name = 'Asuswrt Download Speed' + _unit = 'Mbit/s' + + @property + def unit_of_measurement(self): + """Return the unit of measurement.""" + return self._unit + + async def async_update(self): + """Fetch new state data for the sensor.""" + await super().async_update() + if self._speed is not None: + self._state = round(self._speed[0] / 125000, 2) + + +class AsuswrtTXSensor(AsuswrtSensor): + """Representation of a asuswrt upload speed sensor.""" + + _name = 'Asuswrt Upload Speed' + _unit = 'Mbit/s' + + @property + def unit_of_measurement(self): + """Return the unit of measurement.""" + return self._unit + + async def async_update(self): + """Fetch new state data for the sensor.""" + await super().async_update() + if self._speed is not None: + self._state = round(self._speed[1] / 125000, 2) + + +class AsuswrtTotalRXSensor(AsuswrtSensor): + """Representation of a asuswrt total download sensor.""" + + _name = 'Asuswrt Download' + _unit = 'Gigabyte' + + @property + def unit_of_measurement(self): + """Return the unit of measurement.""" + return self._unit + + async def async_update(self): + """Fetch new state data for the sensor.""" + await super().async_update() + if self._rates is not None: + self._state = round(self._rates[0] / 1000000000, 1) + + +class AsuswrtTotalTXSensor(AsuswrtSensor): + """Representation of a asuswrt total upload sensor.""" + + _name = 'Asuswrt Upload' + _unit = 'Gigabyte' + + @property + def unit_of_measurement(self): + """Return the unit of measurement.""" + return self._unit + + async def async_update(self): + """Fetch new state data for the sensor.""" + await super().async_update() + if self._rates is not None: + self._state = round(self._rates[1] / 1000000000, 1) diff --git a/requirements_all.txt b/requirements_all.txt index 361d84d9fc8..ea278979078 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -85,8 +85,8 @@ abodepy==0.14.0 # homeassistant.components.media_player.frontier_silicon afsapi==0.0.4 -# homeassistant.components.device_tracker.asuswrt -aioasuswrt==1.1.2 +# homeassistant.components.asuswrt +aioasuswrt==1.1.4 # homeassistant.components.device_tracker.automatic aioautomatic==0.6.5 diff --git a/tests/components/device_tracker/test_asuswrt.py b/tests/components/device_tracker/test_asuswrt.py new file mode 100644 index 00000000000..e6f7a582e70 --- /dev/null +++ b/tests/components/device_tracker/test_asuswrt.py @@ -0,0 +1,50 @@ +"""The tests for the ASUSWRT device tracker platform.""" +from homeassistant.setup import async_setup_component + +from homeassistant.components.asuswrt import ( + CONF_PROTOCOL, CONF_MODE, DOMAIN, CONF_PORT, DATA_ASUSWRT) +from homeassistant.const import (CONF_PLATFORM, CONF_PASSWORD, CONF_USERNAME, + CONF_HOST) + +from tests.common import MockDependency, mock_coro_func + +FAKEFILE = None + +VALID_CONFIG_ROUTER_SSH = {DOMAIN: { + CONF_PLATFORM: 'asuswrt', + CONF_HOST: 'fake_host', + CONF_USERNAME: 'fake_user', + CONF_PROTOCOL: 'ssh', + CONF_MODE: 'router', + CONF_PORT: '22' +}} + + +async def test_password_or_pub_key_required(hass): + """Test creating an AsusWRT scanner without a pass or pubkey.""" + with MockDependency('aioasuswrt.asuswrt')as mocked_asus: + mocked_asus.AsusWrt().connection.async_connect = mock_coro_func() + mocked_asus.AsusWrt().is_connected = False + result = await async_setup_component( + hass, DOMAIN, {DOMAIN: { + CONF_HOST: 'fake_host', + CONF_USERNAME: 'fake_user' + }}) + assert not result + + +async def test_get_scanner_with_password_no_pubkey(hass): + """Test creating an AsusWRT scanner with a password and no pubkey.""" + with MockDependency('aioasuswrt.asuswrt')as mocked_asus: + mocked_asus.AsusWrt().connection.async_connect = mock_coro_func() + mocked_asus.AsusWrt( + ).connection.async_get_connected_devices = mock_coro_func( + return_value={}) + result = await async_setup_component( + hass, DOMAIN, {DOMAIN: { + CONF_HOST: 'fake_host', + CONF_USERNAME: 'fake_user', + CONF_PASSWORD: '4321' + }}) + assert result + assert hass.data[DATA_ASUSWRT] is not None