From de55b0587d068db6a6a398538ad5bdcb4bf23763 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Sat, 15 Jun 2019 11:47:58 -0400 Subject: [PATCH 01/21] Add OPNSense device_tracker This commit adds a new component for using an OPNSense router as a device tracker. It uses pyopnsense to query the api to look at the arptable for a list of devices on the network. --- .coveragerc | 1 + homeassistant/components/opnsense/__init__.py | 64 ++++++++++++++++++ .../components/opnsense/device_tracker.py | 67 +++++++++++++++++++ .../components/opnsense/manifest.json | 10 +++ requirements_all.txt | 3 + tests/components/opnsense/__init__.py | 1 + .../opnsense/test_device_tracker.py | 45 +++++++++++++ 7 files changed, 191 insertions(+) create mode 100644 homeassistant/components/opnsense/__init__.py create mode 100644 homeassistant/components/opnsense/device_tracker.py create mode 100644 homeassistant/components/opnsense/manifest.json create mode 100644 tests/components/opnsense/__init__.py create mode 100644 tests/components/opnsense/test_device_tracker.py diff --git a/.coveragerc b/.coveragerc index fc7a4691ef291..9794a2a8ff899 100644 --- a/.coveragerc +++ b/.coveragerc @@ -498,6 +498,7 @@ omit = homeassistant/components/openuv/sensor.py homeassistant/components/openweathermap/sensor.py homeassistant/components/openweathermap/weather.py + homeassistant/components/opnsense/* homeassistant/components/opple/light.py homeassistant/components/orangepi_gpio/* homeassistant/components/oru/* diff --git a/homeassistant/components/opnsense/__init__.py b/homeassistant/components/opnsense/__init__.py new file mode 100644 index 0000000000000..b5357737637c4 --- /dev/null +++ b/homeassistant/components/opnsense/__init__.py @@ -0,0 +1,64 @@ +"""Support for OPNSense Routers.""" +import logging + +import voluptuous as vol + +import homeassistant.helpers.config_validation as cv +from homeassistant.const import (CONF_URL, CONF_API_KEY, CONF_VERIFY_SSL) +from homeassistant.helpers.discovery import async_load_platform + +_LOGGER = logging.getLogger(__name__) + +CONF_API_SECRET = 'api_secret' +CONF_TRACKER_INTERFACE = 'tracker_interfaces' + +DOMAIN = 'opnsense' + +OPNSENSE_DATA = DOMAIN + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + vol.Required(CONF_URL): cv.url, + vol.Required(CONF_API_KEY): cv.string, + vol.Required(CONF_API_SECRET): cv.string, + vol.Optional(CONF_VERIFY_SSL, default=False): cv.boolean, + vol.Optional(CONF_TRACKER_INTERFACE): vol.All( + cv.ensure_list, [cv.string]), + }) +}, extra=vol.ALLOW_EXTRA) + + +def setup(hass, config): + """Set up the opnsense component.""" + conf = config[DOMAIN] + url = conf[CONF_URL] + api_key = conf[CONF_API_KEY] + api_secret = conf[CONF_API_SECRET] + verify_ssl = conf.get(CONF_VERIFY_SSL, False) + tracker_interfaces = conf.get(CONF_TRACKER_INTERFACE, None) + + from pyopnsense import diagnostics + if tracker_interfaces: + # Verify that specified tracker interfaces are valid + netinsight_client = diagnostics.NetworkInsightClient(api_key, + api_secret, + url, verify_ssl) + interfaces = list(netinsight_client.get_interfaces().values()) + for interface in tracker_interfaces: + if interface not in interfaces: + _LOGGER.error('Specified OPNsense tracker interface %s is not ' + 'found', interface) + return False + else: + tracker_interfaces = ['LAN'] + + interfaces_client = diagnostics.InterfaceClient(api_key, api_secret, url, + verify_ssl) + clients = { + 'interfaces': interfaces_client + } + hass.data[OPNSENSE_DATA] = clients + + hass.async_create_task(async_load_platform( + hass, 'device_tracker', DOMAIN, tracker_interfaces, config)) + return True diff --git a/homeassistant/components/opnsense/device_tracker.py b/homeassistant/components/opnsense/device_tracker.py new file mode 100644 index 0000000000000..c8122c3cc20d6 --- /dev/null +++ b/homeassistant/components/opnsense/device_tracker.py @@ -0,0 +1,67 @@ +"""Device tracker support for OPNSense routers.""" +import logging + +from homeassistant.components.device_tracker import DeviceScanner + +from homeassistant.components.opnsense import OPNSENSE_DATA + +_LOGGER = logging.getLogger(__name__) + + +async def async_get_scanner(hass, config, tracker_interfaces=None): + """Configure the OPNSense device_tracker.""" + interface_client = hass.data[OPNSENSE_DATA]['interfaces'] + interfaces = tracker_interfaces or ['LAN'] + scanner = OPNSenseDeviceScanner(interface_client, interfaces) + return scanner + + +class OPNSenseDeviceScanner(DeviceScanner): + """This class queries a router running ASUSWRT firmware.""" + + # Eighth attribute needed for mode (AP mode vs router mode) + def __init__(self, client, interfaces): + """Initialize the scanner.""" + self.last_results = {} + self.success_init = False + self.client = client + self.interfaces = interfaces + + def _get_mac_addrs(self, devices): + out_devices = {} + for device in devices: + if device['intf_description'] in self.interfaces: + out_devices[device['mac']] = device + return out_devices + + def scan_devices(self): + """Scan for new devices and return a list with found device IDs.""" + self.update_info() + return list(self.last_results.keys()) + + def get_device_name(self, device): + """Return the name of the given device or None if we don't know.""" + if device not in self.last_results: + return None + hostname = self.last_results[device].get('hostname') or None + return hostname + + def update_info(self): + """Ensure the information from the OPNSense router is up to date. + + Return boolean if scanning successful. + """ + _LOGGER.info('Checking Devices') + + devices = self.client.get_arp() + self.last_results = self._get_mac_addrs(devices) + return True + + def get_extra_attributes(self, device): + """Return the extra attrs of the given device.""" + if device not in self.last_results: + return None + mfg = self.last_results[device].get('manufacturer') + if mfg: + return {'manufacturer': mfg} + return {} diff --git a/homeassistant/components/opnsense/manifest.json b/homeassistant/components/opnsense/manifest.json new file mode 100644 index 0000000000000..73d33b3eff49c --- /dev/null +++ b/homeassistant/components/opnsense/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "opnsense", + "name": "OPNSense", + "documentation": "https://www.home-assistant.io/components/opnsense", + "requirements": [ + "pyopnsense==0.2.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/requirements_all.txt b/requirements_all.txt index dddbcf7a3fd8d..5b03ab019e494 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1409,6 +1409,9 @@ pyombi==0.1.10 # homeassistant.components.openuv pyopenuv==1.0.9 +# homeassistant.components.opnsense +pyopnsense==0.2.0 + # homeassistant.components.opple pyoppleio==1.0.5 diff --git a/tests/components/opnsense/__init__.py b/tests/components/opnsense/__init__.py new file mode 100644 index 0000000000000..b3c8985caafd3 --- /dev/null +++ b/tests/components/opnsense/__init__.py @@ -0,0 +1 @@ +"""Tests for the opnsense component.""" diff --git a/tests/components/opnsense/test_device_tracker.py b/tests/components/opnsense/test_device_tracker.py new file mode 100644 index 0000000000000..e4b06acb57ff8 --- /dev/null +++ b/tests/components/opnsense/test_device_tracker.py @@ -0,0 +1,45 @@ +"""The tests for the opnsense device tracker platform.""" +from homeassistant.setup import async_setup_component + +from homeassistant.components.opnsense import ( + CONF_API_SECRET, + DOMAIN, + OPNSENSE_DATA, +) +from homeassistant.const import CONF_URL, CONF_API_KEY, CONF_VERIFY_SSL + +from tests.common import MockDependency, mock_coro_func + +FAKEFILE = None + +VALID_CONFIG_ROUTER_SSH = { + DOMAIN: { + CONF_URL: "https://fakehost", + CONF_API_KEY: "fake_key", + CONF_API_SECRET: "fake_secret", + CONF_VERIFY_SSL: False, + } +} + + +async def test_get_scanner(hass): + """Test creating an opnsense scanner.""" + with MockDependency("pyopnsense") as mocked_opnsense: + mocked_opnsense.diagnostic.InterfaceClient().get_arp = mock_coro_func() + mocked_opnsense.diagnostic.NetworkInsightClient().get_interfaces = mock_coro_func( + return_value={} + ) + result = await async_setup_component( + hass, + DOMAIN, + { + DOMAIN: { + CONF_URL: "https://fakehost", + CONF_API_KEY: "fake_key", + CONF_API_SECRET: "fake_secret", + CONF_VERIFY_SSL: False, + } + }, + ) + assert result + assert hass.data[OPNSENSE_DATA] is not None From 1a72bb086a6759bf924bb214952dd0fe478257e2 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Sun, 22 Sep 2019 16:22:44 -0400 Subject: [PATCH 02/21] Run black formatting locally to appease azure --- homeassistant/components/opnsense/__init__.py | 62 +++++++++++-------- .../components/opnsense/device_tracker.py | 16 ++--- .../opnsense/test_device_tracker.py | 6 +- 3 files changed, 44 insertions(+), 40 deletions(-) diff --git a/homeassistant/components/opnsense/__init__.py b/homeassistant/components/opnsense/__init__.py index b5357737637c4..eb92c48d902dd 100644 --- a/homeassistant/components/opnsense/__init__.py +++ b/homeassistant/components/opnsense/__init__.py @@ -4,28 +4,34 @@ import voluptuous as vol import homeassistant.helpers.config_validation as cv -from homeassistant.const import (CONF_URL, CONF_API_KEY, CONF_VERIFY_SSL) +from homeassistant.const import CONF_URL, CONF_API_KEY, CONF_VERIFY_SSL from homeassistant.helpers.discovery import async_load_platform _LOGGER = logging.getLogger(__name__) -CONF_API_SECRET = 'api_secret' -CONF_TRACKER_INTERFACE = 'tracker_interfaces' +CONF_API_SECRET = "api_secret" +CONF_TRACKER_INTERFACE = "tracker_interfaces" -DOMAIN = 'opnsense' +DOMAIN = "opnsense" OPNSENSE_DATA = DOMAIN -CONFIG_SCHEMA = vol.Schema({ - DOMAIN: vol.Schema({ - vol.Required(CONF_URL): cv.url, - vol.Required(CONF_API_KEY): cv.string, - vol.Required(CONF_API_SECRET): cv.string, - vol.Optional(CONF_VERIFY_SSL, default=False): cv.boolean, - vol.Optional(CONF_TRACKER_INTERFACE): vol.All( - cv.ensure_list, [cv.string]), - }) -}, extra=vol.ALLOW_EXTRA) +CONFIG_SCHEMA = vol.Schema( + { + DOMAIN: vol.Schema( + { + vol.Required(CONF_URL): cv.url, + vol.Required(CONF_API_KEY): cv.string, + vol.Required(CONF_API_SECRET): cv.string, + vol.Optional(CONF_VERIFY_SSL, default=False): cv.boolean, + vol.Optional(CONF_TRACKER_INTERFACE): vol.All( + cv.ensure_list, [cv.string] + ), + } + ) + }, + extra=vol.ALLOW_EXTRA, +) def setup(hass, config): @@ -38,27 +44,29 @@ def setup(hass, config): tracker_interfaces = conf.get(CONF_TRACKER_INTERFACE, None) from pyopnsense import diagnostics + if tracker_interfaces: # Verify that specified tracker interfaces are valid - netinsight_client = diagnostics.NetworkInsightClient(api_key, - api_secret, - url, verify_ssl) + netinsight_client = diagnostics.NetworkInsightClient( + api_key, api_secret, url, verify_ssl + ) interfaces = list(netinsight_client.get_interfaces().values()) for interface in tracker_interfaces: if interface not in interfaces: - _LOGGER.error('Specified OPNsense tracker interface %s is not ' - 'found', interface) + _LOGGER.error( + "Specified OPNsense tracker interface %s is not " "found", interface + ) return False else: - tracker_interfaces = ['LAN'] + tracker_interfaces = ["LAN"] - interfaces_client = diagnostics.InterfaceClient(api_key, api_secret, url, - verify_ssl) - clients = { - 'interfaces': interfaces_client - } + interfaces_client = diagnostics.InterfaceClient( + api_key, api_secret, url, verify_ssl + ) + clients = {"interfaces": interfaces_client} hass.data[OPNSENSE_DATA] = clients - hass.async_create_task(async_load_platform( - hass, 'device_tracker', DOMAIN, tracker_interfaces, config)) + hass.async_create_task( + async_load_platform(hass, "device_tracker", DOMAIN, tracker_interfaces, config) + ) return True diff --git a/homeassistant/components/opnsense/device_tracker.py b/homeassistant/components/opnsense/device_tracker.py index c8122c3cc20d6..d68285382e248 100644 --- a/homeassistant/components/opnsense/device_tracker.py +++ b/homeassistant/components/opnsense/device_tracker.py @@ -10,8 +10,8 @@ async def async_get_scanner(hass, config, tracker_interfaces=None): """Configure the OPNSense device_tracker.""" - interface_client = hass.data[OPNSENSE_DATA]['interfaces'] - interfaces = tracker_interfaces or ['LAN'] + interface_client = hass.data[OPNSENSE_DATA]["interfaces"] + interfaces = tracker_interfaces or ["LAN"] scanner = OPNSenseDeviceScanner(interface_client, interfaces) return scanner @@ -30,8 +30,8 @@ def __init__(self, client, interfaces): def _get_mac_addrs(self, devices): out_devices = {} for device in devices: - if device['intf_description'] in self.interfaces: - out_devices[device['mac']] = device + if device["intf_description"] in self.interfaces: + out_devices[device["mac"]] = device return out_devices def scan_devices(self): @@ -43,7 +43,7 @@ def get_device_name(self, device): """Return the name of the given device or None if we don't know.""" if device not in self.last_results: return None - hostname = self.last_results[device].get('hostname') or None + hostname = self.last_results[device].get("hostname") or None return hostname def update_info(self): @@ -51,7 +51,7 @@ def update_info(self): Return boolean if scanning successful. """ - _LOGGER.info('Checking Devices') + _LOGGER.info("Checking Devices") devices = self.client.get_arp() self.last_results = self._get_mac_addrs(devices) @@ -61,7 +61,7 @@ def get_extra_attributes(self, device): """Return the extra attrs of the given device.""" if device not in self.last_results: return None - mfg = self.last_results[device].get('manufacturer') + mfg = self.last_results[device].get("manufacturer") if mfg: - return {'manufacturer': mfg} + return {"manufacturer": mfg} return {} diff --git a/tests/components/opnsense/test_device_tracker.py b/tests/components/opnsense/test_device_tracker.py index e4b06acb57ff8..d2b58d775b67d 100644 --- a/tests/components/opnsense/test_device_tracker.py +++ b/tests/components/opnsense/test_device_tracker.py @@ -1,11 +1,7 @@ """The tests for the opnsense device tracker platform.""" from homeassistant.setup import async_setup_component -from homeassistant.components.opnsense import ( - CONF_API_SECRET, - DOMAIN, - OPNSENSE_DATA, -) +from homeassistant.components.opnsense import CONF_API_SECRET, DOMAIN, OPNSENSE_DATA from homeassistant.const import CONF_URL, CONF_API_KEY, CONF_VERIFY_SSL from tests.common import MockDependency, mock_coro_func From 64a624c4040e4679d05b57f143a8e2f198b97aef Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Mon, 23 Sep 2019 07:53:15 -0400 Subject: [PATCH 03/21] Apply suggestions from code review Co-Authored-By: Fabian Affolter --- homeassistant/components/opnsense/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/opnsense/__init__.py b/homeassistant/components/opnsense/__init__.py index eb92c48d902dd..9eefdd37585fd 100644 --- a/homeassistant/components/opnsense/__init__.py +++ b/homeassistant/components/opnsense/__init__.py @@ -64,7 +64,7 @@ def setup(hass, config): api_key, api_secret, url, verify_ssl ) clients = {"interfaces": interfaces_client} - hass.data[OPNSENSE_DATA] = clients + hass.data[OPNSENSE_DATA] = {"interfaces": interfaces_client} hass.async_create_task( async_load_platform(hass, "device_tracker", DOMAIN, tracker_interfaces, config) From dfa6ba01d7922aeb502042cef70b08334ab9d650 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Mon, 23 Sep 2019 08:06:08 -0400 Subject: [PATCH 04/21] Update homeassistant/components/opnsense/__init__.py Co-Authored-By: Fabian Affolter --- homeassistant/components/opnsense/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/opnsense/__init__.py b/homeassistant/components/opnsense/__init__.py index 9eefdd37585fd..fd5add5b43487 100644 --- a/homeassistant/components/opnsense/__init__.py +++ b/homeassistant/components/opnsense/__init__.py @@ -54,7 +54,7 @@ def setup(hass, config): for interface in tracker_interfaces: if interface not in interfaces: _LOGGER.error( - "Specified OPNsense tracker interface %s is not " "found", interface + "Specified OPNsense tracker interface %s is not found", interface ) return False else: From b93275b24c91776aeddd05a8567a4d0e1ac471e4 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Mon, 23 Sep 2019 08:03:39 -0400 Subject: [PATCH 05/21] Fix issues identified during code review This commit updates several issues found in the module during code review. --- homeassistant/components/opnsense/__init__.py | 5 ++--- homeassistant/components/opnsense/device_tracker.py | 3 ++- homeassistant/components/opnsense/manifest.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/opnsense/__init__.py b/homeassistant/components/opnsense/__init__.py index fd5add5b43487..6b3077e8da985 100644 --- a/homeassistant/components/opnsense/__init__.py +++ b/homeassistant/components/opnsense/__init__.py @@ -1,6 +1,7 @@ """Support for OPNSense Routers.""" import logging +from pyopnsense import diagnostics import voluptuous as vol import homeassistant.helpers.config_validation as cv @@ -40,11 +41,9 @@ def setup(hass, config): url = conf[CONF_URL] api_key = conf[CONF_API_KEY] api_secret = conf[CONF_API_SECRET] - verify_ssl = conf.get(CONF_VERIFY_SSL, False) + verify_ssl = conf.get(CONF_VERIFY_SSL) tracker_interfaces = conf.get(CONF_TRACKER_INTERFACE, None) - from pyopnsense import diagnostics - if tracker_interfaces: # Verify that specified tracker interfaces are valid netinsight_client = diagnostics.NetworkInsightClient( diff --git a/homeassistant/components/opnsense/device_tracker.py b/homeassistant/components/opnsense/device_tracker.py index d68285382e248..4ced3b309279b 100644 --- a/homeassistant/components/opnsense/device_tracker.py +++ b/homeassistant/components/opnsense/device_tracker.py @@ -17,7 +17,7 @@ async def async_get_scanner(hass, config, tracker_interfaces=None): class OPNSenseDeviceScanner(DeviceScanner): - """This class queries a router running ASUSWRT firmware.""" + """This class queries a router running OPNsense.""" # Eighth attribute needed for mode (AP mode vs router mode) def __init__(self, client, interfaces): @@ -28,6 +28,7 @@ def __init__(self, client, interfaces): self.interfaces = interfaces def _get_mac_addrs(self, devices): + """Create dict with mac address keys from list of devices.""" out_devices = {} for device in devices: if device["intf_description"] in self.interfaces: diff --git a/homeassistant/components/opnsense/manifest.json b/homeassistant/components/opnsense/manifest.json index 73d33b3eff49c..36bdac3f3cbee 100644 --- a/homeassistant/components/opnsense/manifest.json +++ b/homeassistant/components/opnsense/manifest.json @@ -6,5 +6,5 @@ "pyopnsense==0.2.0" ], "dependencies": [], - "codeowners": [] + "codeowners": ["@mtreinish"] } From 3dfa4ec5bc255640ae357e7fa04732843ca0a3fe Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Mon, 23 Sep 2019 08:11:59 -0400 Subject: [PATCH 06/21] Update CODEOWNERS for recent changes --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/CODEOWNERS b/CODEOWNERS index 4fbdca2068661..a2b0962c53201 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -238,6 +238,7 @@ homeassistant/components/onboarding/* @home-assistant/core homeassistant/components/opentherm_gw/* @mvn23 homeassistant/components/openuv/* @bachya homeassistant/components/openweathermap/* @fabaff +homeassistant/components/opnsense/* @mtreinish homeassistant/components/orangepi_gpio/* @pascallj homeassistant/components/oru/* @bvlaicu homeassistant/components/owlet/* @oblogic7 From a4e946c7ed0c208c4b5de8a86278dd65a2ae491c Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Mon, 23 Sep 2019 08:12:59 -0400 Subject: [PATCH 07/21] Fix lint --- homeassistant/components/opnsense/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/opnsense/__init__.py b/homeassistant/components/opnsense/__init__.py index 6b3077e8da985..f9f9d14570b0f 100644 --- a/homeassistant/components/opnsense/__init__.py +++ b/homeassistant/components/opnsense/__init__.py @@ -62,7 +62,6 @@ def setup(hass, config): interfaces_client = diagnostics.InterfaceClient( api_key, api_secret, url, verify_ssl ) - clients = {"interfaces": interfaces_client} hass.data[OPNSENSE_DATA] = {"interfaces": interfaces_client} hass.async_create_task( From fca58384401376bfaaa099ba89ff1f51b6de8624 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Mon, 23 Sep 2019 09:19:13 -0400 Subject: [PATCH 08/21] Apply suggestions from code review Co-Authored-By: Martin Hjelmare --- homeassistant/components/opnsense/__init__.py | 2 +- homeassistant/components/opnsense/device_tracker.py | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/opnsense/__init__.py b/homeassistant/components/opnsense/__init__.py index f9f9d14570b0f..1993f43efac95 100644 --- a/homeassistant/components/opnsense/__init__.py +++ b/homeassistant/components/opnsense/__init__.py @@ -41,7 +41,7 @@ def setup(hass, config): url = conf[CONF_URL] api_key = conf[CONF_API_KEY] api_secret = conf[CONF_API_SECRET] - verify_ssl = conf.get(CONF_VERIFY_SSL) + verify_ssl = conf[CONF_VERIFY_SSL] tracker_interfaces = conf.get(CONF_TRACKER_INTERFACE, None) if tracker_interfaces: diff --git a/homeassistant/components/opnsense/device_tracker.py b/homeassistant/components/opnsense/device_tracker.py index 4ced3b309279b..546af1dd75dbd 100644 --- a/homeassistant/components/opnsense/device_tracker.py +++ b/homeassistant/components/opnsense/device_tracker.py @@ -8,11 +8,10 @@ _LOGGER = logging.getLogger(__name__) -async def async_get_scanner(hass, config, tracker_interfaces=None): +async def async_get_scanner(hass, config, discovery_info=None): """Configure the OPNSense device_tracker.""" interface_client = hass.data[OPNSENSE_DATA]["interfaces"] - interfaces = tracker_interfaces or ["LAN"] - scanner = OPNSenseDeviceScanner(interface_client, interfaces) + scanner = OPNSenseDeviceScanner(interface_client, discovery_info) return scanner @@ -23,7 +22,6 @@ class OPNSenseDeviceScanner(DeviceScanner): def __init__(self, client, interfaces): """Initialize the scanner.""" self.last_results = {} - self.success_init = False self.client = client self.interfaces = interfaces @@ -38,7 +36,7 @@ def _get_mac_addrs(self, devices): def scan_devices(self): """Scan for new devices and return a list with found device IDs.""" self.update_info() - return list(self.last_results.keys()) + return list(self.last_results) def get_device_name(self, device): """Return the name of the given device or None if we don't know.""" @@ -52,11 +50,9 @@ def update_info(self): Return boolean if scanning successful. """ - _LOGGER.info("Checking Devices") devices = self.client.get_arp() self.last_results = self._get_mac_addrs(devices) - return True def get_extra_attributes(self, device): """Return the extra attrs of the given device.""" From ef3ed62a04cd4888cb698f05f43733a6166ee138 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Mon, 23 Sep 2019 10:25:03 -0400 Subject: [PATCH 09/21] More fixes from review comments This commit fixes several issues from review comments, including abandoning all the use of async code. This also completely reworks the tests to be a bit clearer. --- homeassistant/components/opnsense/__init__.py | 12 ++--- .../components/opnsense/device_tracker.py | 5 ++- .../opnsense/test_device_tracker.py | 45 ++++++++++--------- 3 files changed, 30 insertions(+), 32 deletions(-) diff --git a/homeassistant/components/opnsense/__init__.py b/homeassistant/components/opnsense/__init__.py index 1993f43efac95..eff51f69afef4 100644 --- a/homeassistant/components/opnsense/__init__.py +++ b/homeassistant/components/opnsense/__init__.py @@ -6,7 +6,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.const import CONF_URL, CONF_API_KEY, CONF_VERIFY_SSL -from homeassistant.helpers.discovery import async_load_platform +from homeassistant.helpers.discovery import load_platform _LOGGER = logging.getLogger(__name__) @@ -25,7 +25,7 @@ vol.Required(CONF_API_KEY): cv.string, vol.Required(CONF_API_SECRET): cv.string, vol.Optional(CONF_VERIFY_SSL, default=False): cv.boolean, - vol.Optional(CONF_TRACKER_INTERFACE): vol.All( + vol.Optional(CONF_TRACKER_INTERFACE, default=[]): vol.All( cv.ensure_list, [cv.string] ), } @@ -42,7 +42,7 @@ def setup(hass, config): api_key = conf[CONF_API_KEY] api_secret = conf[CONF_API_SECRET] verify_ssl = conf[CONF_VERIFY_SSL] - tracker_interfaces = conf.get(CONF_TRACKER_INTERFACE, None) + tracker_interfaces = conf[CONF_TRACKER_INTERFACE] if tracker_interfaces: # Verify that specified tracker interfaces are valid @@ -56,15 +56,11 @@ def setup(hass, config): "Specified OPNsense tracker interface %s is not found", interface ) return False - else: - tracker_interfaces = ["LAN"] interfaces_client = diagnostics.InterfaceClient( api_key, api_secret, url, verify_ssl ) hass.data[OPNSENSE_DATA] = {"interfaces": interfaces_client} - hass.async_create_task( - async_load_platform(hass, "device_tracker", DOMAIN, tracker_interfaces, config) - ) + load_platform(hass, "device_tracker", DOMAIN, tracker_interfaces, config) return True diff --git a/homeassistant/components/opnsense/device_tracker.py b/homeassistant/components/opnsense/device_tracker.py index 546af1dd75dbd..93aecef711892 100644 --- a/homeassistant/components/opnsense/device_tracker.py +++ b/homeassistant/components/opnsense/device_tracker.py @@ -18,7 +18,6 @@ async def async_get_scanner(hass, config, discovery_info=None): class OPNSenseDeviceScanner(DeviceScanner): """This class queries a router running OPNsense.""" - # Eighth attribute needed for mode (AP mode vs router mode) def __init__(self, client, interfaces): """Initialize the scanner.""" self.last_results = {} @@ -29,7 +28,9 @@ def _get_mac_addrs(self, devices): """Create dict with mac address keys from list of devices.""" out_devices = {} for device in devices: - if device["intf_description"] in self.interfaces: + if not self.interfaces: + out_devices[device["mac"]] = device + elif device["intf_description"] in self.interfaces: out_devices[device["mac"]] = device return out_devices diff --git a/tests/components/opnsense/test_device_tracker.py b/tests/components/opnsense/test_device_tracker.py index d2b58d775b67d..4d2a2e461068a 100644 --- a/tests/components/opnsense/test_device_tracker.py +++ b/tests/components/opnsense/test_device_tracker.py @@ -1,36 +1,36 @@ """The tests for the opnsense device tracker platform.""" -from homeassistant.setup import async_setup_component +import unittest +from unittest import mock + +from homeassistant.components import opnsense from homeassistant.components.opnsense import CONF_API_SECRET, DOMAIN, OPNSENSE_DATA from homeassistant.const import CONF_URL, CONF_API_KEY, CONF_VERIFY_SSL +from homeassistant.setup import setup_component -from tests.common import MockDependency, mock_coro_func +from tests.common import get_test_home_assistant -FAKEFILE = None -VALID_CONFIG_ROUTER_SSH = { - DOMAIN: { - CONF_URL: "https://fakehost", - CONF_API_KEY: "fake_key", - CONF_API_SECRET: "fake_secret", - CONF_VERIFY_SSL: False, - } -} +class TestOpnSenseDeviceTrackerSetup(unittest.TestCase): + """Test opnsense device tracker setup.""" + def setUp(self): + """Set up things to be run when tests are started.""" + self.hass = get_test_home_assistant() -async def test_get_scanner(hass): - """Test creating an opnsense scanner.""" - with MockDependency("pyopnsense") as mocked_opnsense: - mocked_opnsense.diagnostic.InterfaceClient().get_arp = mock_coro_func() - mocked_opnsense.diagnostic.NetworkInsightClient().get_interfaces = mock_coro_func( - return_value={} - ) - result = await async_setup_component( - hass, + def tearDown(self): + """Stop everything that was started.""" + self.hass.stop() + + @mock.patch.object(opnsense, 'diagnostics') + def test_get_scanner(self, pyopnsense_mock): + """Test creating an opnsense scanner.""" + result = setup_component( + self.hass, DOMAIN, { DOMAIN: { - CONF_URL: "https://fakehost", + CONF_URL: "https://fake_host_fun/api", CONF_API_KEY: "fake_key", CONF_API_SECRET: "fake_secret", CONF_VERIFY_SSL: False, @@ -38,4 +38,5 @@ async def test_get_scanner(hass): }, ) assert result - assert hass.data[OPNSENSE_DATA] is not None + assert self.hass.data[OPNSENSE_DATA] is not None + assert pyopnsense_mock.has_calls() From 18d4f041f7e0a04d34fb8a1dd415aba0ed691b9b Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 2 Oct 2019 08:28:13 -0400 Subject: [PATCH 10/21] Revert tests to previous format --- .../opnsense/test_device_tracker.py | 37 ++++++++----------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/tests/components/opnsense/test_device_tracker.py b/tests/components/opnsense/test_device_tracker.py index 4d2a2e461068a..b3b44f94e9a5b 100644 --- a/tests/components/opnsense/test_device_tracker.py +++ b/tests/components/opnsense/test_device_tracker.py @@ -1,32 +1,26 @@ """The tests for the opnsense device tracker platform.""" -import unittest from unittest import mock -from homeassistant.components import opnsense from homeassistant.components.opnsense import CONF_API_SECRET, DOMAIN, OPNSENSE_DATA from homeassistant.const import CONF_URL, CONF_API_KEY, CONF_VERIFY_SSL -from homeassistant.setup import setup_component +from homeassistant.setup import async_setup_component -from tests.common import get_test_home_assistant +from tests.common import MockDependency -class TestOpnSenseDeviceTrackerSetup(unittest.TestCase): - """Test opnsense device tracker setup.""" - - def setUp(self): - """Set up things to be run when tests are started.""" - self.hass = get_test_home_assistant() - - def tearDown(self): - """Stop everything that was started.""" - self.hass.stop() - - @mock.patch.object(opnsense, 'diagnostics') - def test_get_scanner(self, pyopnsense_mock): - """Test creating an opnsense scanner.""" - result = setup_component( - self.hass, +async def test_get_scanner(hass): + """Test creating an opnsense scanner.""" + with MockDependency("pyopnsense") as mocked_opnsense: + get_arp = mock.MagicMock() + get_interfaces = mock.MagicMock() + get_interfaces.return_value = {} + mocked_opnsense.diagnostic.InterfaceClient().get_arp = get_arp + mocked_opnsense.diagnostic.NetworkInsightClient().get_interfaces = ( + get_interfaces + ) + result = await async_setup_component( + hass, DOMAIN, { DOMAIN: { @@ -38,5 +32,4 @@ def test_get_scanner(self, pyopnsense_mock): }, ) assert result - assert self.hass.data[OPNSENSE_DATA] is not None - assert pyopnsense_mock.has_calls() + assert hass.data[OPNSENSE_DATA] is not None From f4e91cd52bc36ba80de069f5acddd5ac9347daac Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 2 Oct 2019 12:26:18 -0400 Subject: [PATCH 11/21] Add device detection to opnsense device_tracker test This commit adds actual device detection to the unit test for the setup test. A fake api response is added to mocks for both api clients so that they will register devices as expected and asserts are added for that. The pyopnsense import is moved from the module level to be runtime in the class. This was done because it was the only way to make the MockDependency() call work as expected. --- homeassistant/components/opnsense/__init__.py | 4 +- .../opnsense/test_device_tracker.py | 37 ++++++++++++++----- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/opnsense/__init__.py b/homeassistant/components/opnsense/__init__.py index eff51f69afef4..3ec4149aac6e6 100644 --- a/homeassistant/components/opnsense/__init__.py +++ b/homeassistant/components/opnsense/__init__.py @@ -1,7 +1,6 @@ """Support for OPNSense Routers.""" import logging -from pyopnsense import diagnostics import voluptuous as vol import homeassistant.helpers.config_validation as cv @@ -37,6 +36,9 @@ def setup(hass, config): """Set up the opnsense component.""" + + from pyopnsense import diagnostics + conf = config[DOMAIN] url = conf[CONF_URL] api_key = conf[CONF_API_KEY] diff --git a/tests/components/opnsense/test_device_tracker.py b/tests/components/opnsense/test_device_tracker.py index b3b44f94e9a5b..e3c79befae99a 100644 --- a/tests/components/opnsense/test_device_tracker.py +++ b/tests/components/opnsense/test_device_tracker.py @@ -5,6 +5,7 @@ from homeassistant.components.opnsense import CONF_API_SECRET, DOMAIN, OPNSENSE_DATA from homeassistant.const import CONF_URL, CONF_API_KEY, CONF_VERIFY_SSL from homeassistant.setup import async_setup_component +import homeassistant.components.device_tracker as device_tracker from tests.common import MockDependency @@ -12,16 +13,30 @@ async def test_get_scanner(hass): """Test creating an opnsense scanner.""" with MockDependency("pyopnsense") as mocked_opnsense: - get_arp = mock.MagicMock() - get_interfaces = mock.MagicMock() - get_interfaces.return_value = {} - mocked_opnsense.diagnostic.InterfaceClient().get_arp = get_arp - mocked_opnsense.diagnostic.NetworkInsightClient().get_interfaces = ( - get_interfaces - ) + interface_client = mock.MagicMock() + mocked_opnsense.diagnostics.InterfaceClient.return_value = interface_client + interface_client.get_arp.return_value = [ + {'hostname': '', + 'intf': 'igb1', + 'intf_description': 'LAN', + 'ip': '192.168.0.123', + 'mac': 'ff:ff:ff:ff:ff:ff', + 'manufacturer': ''}, + {'hostname': 'Desktop', + 'intf': 'igb1', + 'intf_description': 'LAN', + 'ip': '192.168.0.167', + 'mac': 'ff:ff:ff:ff:ff:fe', + 'manufacturer': 'OEM'}] + network_insight_client = mock.MagicMock() + mocked_opnsense.diagnostics.NetworkInsightClient = network_insight_client + network_insight_client.get_interfaces.return_value = { + 'igb0': 'WAN', + 'igb1': 'LAN'} + result = await async_setup_component( hass, - DOMAIN, + device_tracker.DOMAIN, { DOMAIN: { CONF_URL: "https://fake_host_fun/api", @@ -32,4 +47,8 @@ async def test_get_scanner(hass): }, ) assert result - assert hass.data[OPNSENSE_DATA] is not None + device_1 = hass.states.get('device_tracker.desktop') + assert device_1 is not None + assert device_1.state == 'not_home' + device_2 = hass.states.get('device_tracker.ff_ff_ff_ff_ff_ff') + assert device_2.state == 'not_home' From 60339b4fa8c5fda2679b84f0e7f7b0e057092edc Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 2 Oct 2019 12:36:09 -0400 Subject: [PATCH 12/21] Rerun black --- .../opnsense/test_device_tracker.py | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/tests/components/opnsense/test_device_tracker.py b/tests/components/opnsense/test_device_tracker.py index e3c79befae99a..eac67f51720e4 100644 --- a/tests/components/opnsense/test_device_tracker.py +++ b/tests/components/opnsense/test_device_tracker.py @@ -16,23 +16,29 @@ async def test_get_scanner(hass): interface_client = mock.MagicMock() mocked_opnsense.diagnostics.InterfaceClient.return_value = interface_client interface_client.get_arp.return_value = [ - {'hostname': '', - 'intf': 'igb1', - 'intf_description': 'LAN', - 'ip': '192.168.0.123', - 'mac': 'ff:ff:ff:ff:ff:ff', - 'manufacturer': ''}, - {'hostname': 'Desktop', - 'intf': 'igb1', - 'intf_description': 'LAN', - 'ip': '192.168.0.167', - 'mac': 'ff:ff:ff:ff:ff:fe', - 'manufacturer': 'OEM'}] + { + "hostname": "", + "intf": "igb1", + "intf_description": "LAN", + "ip": "192.168.0.123", + "mac": "ff:ff:ff:ff:ff:ff", + "manufacturer": "", + }, + { + "hostname": "Desktop", + "intf": "igb1", + "intf_description": "LAN", + "ip": "192.168.0.167", + "mac": "ff:ff:ff:ff:ff:fe", + "manufacturer": "OEM", + }, + ] network_insight_client = mock.MagicMock() mocked_opnsense.diagnostics.NetworkInsightClient = network_insight_client network_insight_client.get_interfaces.return_value = { - 'igb0': 'WAN', - 'igb1': 'LAN'} + "igb0": "WAN", + "igb1": "LAN", + } result = await async_setup_component( hass, @@ -47,8 +53,8 @@ async def test_get_scanner(hass): }, ) assert result - device_1 = hass.states.get('device_tracker.desktop') + device_1 = hass.states.get("device_tracker.desktop") assert device_1 is not None - assert device_1.state == 'not_home' - device_2 = hass.states.get('device_tracker.ff_ff_ff_ff_ff_ff') - assert device_2.state == 'not_home' + assert device_1.state == "not_home" + device_2 = hass.states.get("device_tracker.ff_ff_ff_ff_ff_ff") + assert device_2.state == "not_home" From eb4a851a2b943408e6376e7b4d4be2a22ffe8d1e Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 2 Oct 2019 12:37:34 -0400 Subject: [PATCH 13/21] Fix lint --- tests/components/opnsense/test_device_tracker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/components/opnsense/test_device_tracker.py b/tests/components/opnsense/test_device_tracker.py index eac67f51720e4..bd2eedfb8782a 100644 --- a/tests/components/opnsense/test_device_tracker.py +++ b/tests/components/opnsense/test_device_tracker.py @@ -2,7 +2,7 @@ from unittest import mock -from homeassistant.components.opnsense import CONF_API_SECRET, DOMAIN, OPNSENSE_DATA +from homeassistant.components.opnsense import CONF_API_SECRET, DOMAIN from homeassistant.const import CONF_URL, CONF_API_KEY, CONF_VERIFY_SSL from homeassistant.setup import async_setup_component import homeassistant.components.device_tracker as device_tracker From c8bc10fccf14f053d382bf1503f64c8fd53aace1 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 2 Oct 2019 15:22:53 -0400 Subject: [PATCH 14/21] Move import back to module level --- homeassistant/components/opnsense/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/opnsense/__init__.py b/homeassistant/components/opnsense/__init__.py index 3ec4149aac6e6..d4f6112e755f6 100644 --- a/homeassistant/components/opnsense/__init__.py +++ b/homeassistant/components/opnsense/__init__.py @@ -1,6 +1,7 @@ """Support for OPNSense Routers.""" import logging +from pyopnsense import diagnostics import voluptuous as vol import homeassistant.helpers.config_validation as cv @@ -37,8 +38,6 @@ def setup(hass, config): """Set up the opnsense component.""" - from pyopnsense import diagnostics - conf = config[DOMAIN] url = conf[CONF_URL] api_key = conf[CONF_API_KEY] From 8bb1c9ec9669450d0e8d60e4d91301cf136c4dc0 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 2 Oct 2019 18:48:36 -0400 Subject: [PATCH 15/21] Return false on configuration errors in setup This commit updates the connection logic to return false if we're unable to connect to the configured OPNsense API endpoint for any reason. Previously we would not catch if an endpoint was incorrectly configured until we first tried to use it. In this case it would raise an unhandled exception. To handle this more gracefully this adds an api call early in the setup and catches any exception raised by that so we can return False to indicate the setup failed. --- homeassistant/components/opnsense/__init__.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/opnsense/__init__.py b/homeassistant/components/opnsense/__init__.py index d4f6112e755f6..50131fa15d76e 100644 --- a/homeassistant/components/opnsense/__init__.py +++ b/homeassistant/components/opnsense/__init__.py @@ -45,6 +45,15 @@ def setup(hass, config): verify_ssl = conf[CONF_VERIFY_SSL] tracker_interfaces = conf[CONF_TRACKER_INTERFACE] + interfaces_client = diagnostics.InterfaceClient( + api_key, api_secret, url, verify_ssl + ) + try: + interfaces_client.get_arp() + except Exception: # pylint: disable=broad-except + _LOGGER.exception("Failure while connecting to OPNsense API endpoint.") + return False + if tracker_interfaces: # Verify that specified tracker interfaces are valid netinsight_client = diagnostics.NetworkInsightClient( @@ -58,9 +67,6 @@ def setup(hass, config): ) return False - interfaces_client = diagnostics.InterfaceClient( - api_key, api_secret, url, verify_ssl - ) hass.data[OPNSENSE_DATA] = {"interfaces": interfaces_client} load_platform(hass, "device_tracker", DOMAIN, tracker_interfaces, config) From ed487514fd1f357ae94c26ea520d369c25309d8e Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 3 Oct 2019 10:39:17 -0400 Subject: [PATCH 16/21] Update tests --- homeassistant/components/opnsense/__init__.py | 5 +- .../components/opnsense/device_tracker.py | 6 +- .../opnsense/test_device_tracker.py | 102 +++++++++--------- 3 files changed, 61 insertions(+), 52 deletions(-) diff --git a/homeassistant/components/opnsense/__init__.py b/homeassistant/components/opnsense/__init__.py index 50131fa15d76e..e74ff1dddf314 100644 --- a/homeassistant/components/opnsense/__init__.py +++ b/homeassistant/components/opnsense/__init__.py @@ -67,7 +67,10 @@ def setup(hass, config): ) return False - hass.data[OPNSENSE_DATA] = {"interfaces": interfaces_client} + hass.data[OPNSENSE_DATA] = { + "interfaces": interfaces_client, + CONF_TRACKER_INTERFACE: tracker_interfaces, + } load_platform(hass, "device_tracker", DOMAIN, tracker_interfaces, config) return True diff --git a/homeassistant/components/opnsense/device_tracker.py b/homeassistant/components/opnsense/device_tracker.py index 93aecef711892..d271e33ea5489 100644 --- a/homeassistant/components/opnsense/device_tracker.py +++ b/homeassistant/components/opnsense/device_tracker.py @@ -3,7 +3,7 @@ from homeassistant.components.device_tracker import DeviceScanner -from homeassistant.components.opnsense import OPNSENSE_DATA +from homeassistant.components.opnsense import OPNSENSE_DATA, CONF_TRACKER_INTERFACE _LOGGER = logging.getLogger(__name__) @@ -11,7 +11,9 @@ async def async_get_scanner(hass, config, discovery_info=None): """Configure the OPNSense device_tracker.""" interface_client = hass.data[OPNSENSE_DATA]["interfaces"] - scanner = OPNSenseDeviceScanner(interface_client, discovery_info) + scanner = OPNSenseDeviceScanner( + interface_client, hass.data[OPNSENSE_DATA][CONF_TRACKER_INTERFACE] + ) return scanner diff --git a/tests/components/opnsense/test_device_tracker.py b/tests/components/opnsense/test_device_tracker.py index bd2eedfb8782a..016d57432fca8 100644 --- a/tests/components/opnsense/test_device_tracker.py +++ b/tests/components/opnsense/test_device_tracker.py @@ -2,59 +2,63 @@ from unittest import mock +import pytest + +from homeassistant.components import opnsense from homeassistant.components.opnsense import CONF_API_SECRET, DOMAIN from homeassistant.const import CONF_URL, CONF_API_KEY, CONF_VERIFY_SSL from homeassistant.setup import async_setup_component -import homeassistant.components.device_tracker as device_tracker -from tests.common import MockDependency + +@pytest.fixture(name="mocked_opnsense") +def mocked_opnsense(): + """Mock for pyopnense.diagnostics.""" + with mock.patch.object(opnsense, "diagnostics") as mocked_opn: + yield mocked_opn -async def test_get_scanner(hass): +async def test_get_scanner(hass, mocked_opnsense): """Test creating an opnsense scanner.""" - with MockDependency("pyopnsense") as mocked_opnsense: - interface_client = mock.MagicMock() - mocked_opnsense.diagnostics.InterfaceClient.return_value = interface_client - interface_client.get_arp.return_value = [ - { - "hostname": "", - "intf": "igb1", - "intf_description": "LAN", - "ip": "192.168.0.123", - "mac": "ff:ff:ff:ff:ff:ff", - "manufacturer": "", - }, - { - "hostname": "Desktop", - "intf": "igb1", - "intf_description": "LAN", - "ip": "192.168.0.167", - "mac": "ff:ff:ff:ff:ff:fe", - "manufacturer": "OEM", - }, - ] - network_insight_client = mock.MagicMock() - mocked_opnsense.diagnostics.NetworkInsightClient = network_insight_client - network_insight_client.get_interfaces.return_value = { - "igb0": "WAN", - "igb1": "LAN", - } - - result = await async_setup_component( - hass, - device_tracker.DOMAIN, - { - DOMAIN: { - CONF_URL: "https://fake_host_fun/api", - CONF_API_KEY: "fake_key", - CONF_API_SECRET: "fake_secret", - CONF_VERIFY_SSL: False, - } - }, - ) - assert result - device_1 = hass.states.get("device_tracker.desktop") - assert device_1 is not None - assert device_1.state == "not_home" - device_2 = hass.states.get("device_tracker.ff_ff_ff_ff_ff_ff") - assert device_2.state == "not_home" + interface_client = mock.MagicMock() + mocked_opnsense.InterfaceClient.return_value = interface_client + interface_client.get_arp.return_value = [ + { + "hostname": "", + "intf": "igb1", + "intf_description": "LAN", + "ip": "192.168.0.123", + "mac": "ff:ff:ff:ff:ff:ff", + "manufacturer": "", + }, + { + "hostname": "Desktop", + "intf": "igb1", + "intf_description": "LAN", + "ip": "192.168.0.167", + "mac": "ff:ff:ff:ff:ff:fe", + "manufacturer": "OEM", + }, + ] + network_insight_client = mock.MagicMock() + mocked_opnsense.NetworkInsightClient.return_value = network_insight_client + network_insight_client.get_interfaces.return_value = {"igb0": "WAN", "igb1": "LAN"} + + result = await async_setup_component( + hass, + DOMAIN, + { + DOMAIN: { + CONF_URL: "https://fake_host_fun/api", + CONF_API_KEY: "fake_key", + CONF_API_SECRET: "fake_secret", + CONF_VERIFY_SSL: False, + } + }, + ) + await hass.async_block_till_done() + assert result + device_1 = hass.states.get("device_tracker.desktop") + assert device_1 is not None + assert device_1.state == "home" + device_2 = hass.states.get("device_tracker.ff_ff_ff_ff_ff_ff") + assert device_2.state == "home" From 595c26c161b44cf9c6533675d6254cc2336e5ea3 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 3 Oct 2019 12:11:05 -0400 Subject: [PATCH 17/21] Add pyopnsense to test requirements --- requirements_test_all.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e5361e7464e80..09c753b9db55b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -475,6 +475,9 @@ pyopenuv==1.0.9 # homeassistant.components.opentherm_gw pyotgw==0.5b1 +# homeassistant.components.opnsense +pyopnsense==0.2.0 + # homeassistant.auth.mfa_modules.notify # homeassistant.auth.mfa_modules.totp # homeassistant.components.otp From 5b368c6b53d4f568ee37d76434fb098aecae2376 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 4 Dec 2019 08:14:42 -0500 Subject: [PATCH 18/21] Rerun gen_requirements script --- requirements_test_all.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 09c753b9db55b..80381d39bc3c5 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -472,12 +472,12 @@ pynx584==0.4 # homeassistant.components.openuv pyopenuv==1.0.9 -# homeassistant.components.opentherm_gw -pyotgw==0.5b1 - # homeassistant.components.opnsense pyopnsense==0.2.0 +# homeassistant.components.opentherm_gw +pyotgw==0.5b1 + # homeassistant.auth.mfa_modules.notify # homeassistant.auth.mfa_modules.totp # homeassistant.components.otp From 9309a6aa24a8cd313d26f391d2062485b7fd734e Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Sun, 22 Dec 2019 16:02:39 -0500 Subject: [PATCH 19/21] Fix failing isort lint job step Since opening the PR originally yet another lint/style checker was added which failed the PR in CI. This commit makes the adjustments to have this pass the additional tool's checks. --- homeassistant/components/opnsense/__init__.py | 2 +- homeassistant/components/opnsense/device_tracker.py | 3 +-- tests/components/opnsense/test_device_tracker.py | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/opnsense/__init__.py b/homeassistant/components/opnsense/__init__.py index e74ff1dddf314..04d4bb7f86ad5 100644 --- a/homeassistant/components/opnsense/__init__.py +++ b/homeassistant/components/opnsense/__init__.py @@ -4,8 +4,8 @@ from pyopnsense import diagnostics import voluptuous as vol +from homeassistant.const import CONF_API_KEY, CONF_URL, CONF_VERIFY_SSL import homeassistant.helpers.config_validation as cv -from homeassistant.const import CONF_URL, CONF_API_KEY, CONF_VERIFY_SSL from homeassistant.helpers.discovery import load_platform _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/opnsense/device_tracker.py b/homeassistant/components/opnsense/device_tracker.py index d271e33ea5489..c64e0b0679a75 100644 --- a/homeassistant/components/opnsense/device_tracker.py +++ b/homeassistant/components/opnsense/device_tracker.py @@ -2,8 +2,7 @@ import logging from homeassistant.components.device_tracker import DeviceScanner - -from homeassistant.components.opnsense import OPNSENSE_DATA, CONF_TRACKER_INTERFACE +from homeassistant.components.opnsense import CONF_TRACKER_INTERFACE, OPNSENSE_DATA _LOGGER = logging.getLogger(__name__) diff --git a/tests/components/opnsense/test_device_tracker.py b/tests/components/opnsense/test_device_tracker.py index 016d57432fca8..122a9bf294c3d 100644 --- a/tests/components/opnsense/test_device_tracker.py +++ b/tests/components/opnsense/test_device_tracker.py @@ -6,7 +6,7 @@ from homeassistant.components import opnsense from homeassistant.components.opnsense import CONF_API_SECRET, DOMAIN -from homeassistant.const import CONF_URL, CONF_API_KEY, CONF_VERIFY_SSL +from homeassistant.const import CONF_API_KEY, CONF_URL, CONF_VERIFY_SSL from homeassistant.setup import async_setup_component From 84bfcddb01580dbdfdcea18b57dbefc73d269e86 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 29 Jan 2020 14:27:20 +0100 Subject: [PATCH 20/21] Fix comment --- homeassistant/components/opnsense/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/opnsense/__init__.py b/homeassistant/components/opnsense/__init__.py index 04d4bb7f86ad5..608bca0f03b2a 100644 --- a/homeassistant/components/opnsense/__init__.py +++ b/homeassistant/components/opnsense/__init__.py @@ -2,6 +2,7 @@ import logging from pyopnsense import diagnostics +from pyopnsense.exceptions import APIException import voluptuous as vol from homeassistant.const import CONF_API_KEY, CONF_URL, CONF_VERIFY_SSL @@ -50,7 +51,7 @@ def setup(hass, config): ) try: interfaces_client.get_arp() - except Exception: # pylint: disable=broad-except + except APIException: _LOGGER.exception("Failure while connecting to OPNsense API endpoint.") return False From 5936635c4db1fc63d36a472f6fece1ddb0a56bd7 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 29 Jan 2020 14:30:45 +0100 Subject: [PATCH 21/21] Update manifest.json --- homeassistant/components/opnsense/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/opnsense/manifest.json b/homeassistant/components/opnsense/manifest.json index 36bdac3f3cbee..858316801029f 100644 --- a/homeassistant/components/opnsense/manifest.json +++ b/homeassistant/components/opnsense/manifest.json @@ -1,7 +1,7 @@ { "domain": "opnsense", "name": "OPNSense", - "documentation": "https://www.home-assistant.io/components/opnsense", + "documentation": "https://www.home-assistant.io/integrations/opnsense", "requirements": [ "pyopnsense==0.2.0" ],