-
-
Notifications
You must be signed in to change notification settings - Fork 37.7k
Add OPNSense device tracker #26834
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Add OPNSense device tracker #26834
Changes from 20 commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
de55b05
Add OPNSense device_tracker
mtreinish 1a72bb0
Run black formatting locally to appease azure
mtreinish 64a624c
Apply suggestions from code review
mtreinish dfa6ba0
Update homeassistant/components/opnsense/__init__.py
mtreinish b93275b
Fix issues identified during code review
mtreinish 3dfa4ec
Update CODEOWNERS for recent changes
mtreinish a4e946c
Fix lint
mtreinish fca5838
Apply suggestions from code review
mtreinish ef3ed62
More fixes from review comments
mtreinish 18d4f04
Revert tests to previous format
mtreinish f4e91cd
Add device detection to opnsense device_tracker test
mtreinish 60339b4
Rerun black
mtreinish eb4a851
Fix lint
mtreinish c8bc10f
Move import back to module level
mtreinish 8bb1c9e
Return false on configuration errors in setup
mtreinish ed48751
Update tests
mtreinish 595c26c
Add pyopnsense to test requirements
mtreinish 5b368c6
Rerun gen_requirements script
mtreinish 9309a6a
Fix failing isort lint job step
mtreinish 84bfcdd
Fix comment
pvizeli 5936635
Update manifest.json
pvizeli File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| """Support for OPNSense Routers.""" | ||
| 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 | ||
| import homeassistant.helpers.config_validation as cv | ||
| from homeassistant.helpers.discovery import 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, default=[]): 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[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 APIException: | ||
| _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( | ||
| 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 | ||
|
|
||
| hass.data[OPNSENSE_DATA] = { | ||
| "interfaces": interfaces_client, | ||
| CONF_TRACKER_INTERFACE: tracker_interfaces, | ||
| } | ||
|
|
||
| load_platform(hass, "device_tracker", DOMAIN, tracker_interfaces, config) | ||
| return True | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| """Device tracker support for OPNSense routers.""" | ||
| import logging | ||
|
|
||
| from homeassistant.components.device_tracker import DeviceScanner | ||
| from homeassistant.components.opnsense import CONF_TRACKER_INTERFACE, OPNSENSE_DATA | ||
|
|
||
| _LOGGER = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| 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, hass.data[OPNSENSE_DATA][CONF_TRACKER_INTERFACE] | ||
| ) | ||
| return scanner | ||
|
|
||
|
|
||
| class OPNSenseDeviceScanner(DeviceScanner): | ||
| """This class queries a router running OPNsense.""" | ||
|
|
||
| def __init__(self, client, interfaces): | ||
| """Initialize the scanner.""" | ||
| self.last_results = {} | ||
| self.client = client | ||
| self.interfaces = interfaces | ||
|
|
||
| def _get_mac_addrs(self, devices): | ||
|
mtreinish marked this conversation as resolved.
|
||
| """Create dict with mac address keys from list of devices.""" | ||
| out_devices = {} | ||
| for device in devices: | ||
| if not self.interfaces: | ||
| out_devices[device["mac"]] = device | ||
| elif 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) | ||
|
|
||
| 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. | ||
| """ | ||
|
|
||
| devices = self.client.get_arp() | ||
| self.last_results = self._get_mac_addrs(devices) | ||
|
|
||
| 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 {} | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| { | ||
| "domain": "opnsense", | ||
| "name": "OPNSense", | ||
| "documentation": "https://www.home-assistant.io/components/opnsense", | ||
| "requirements": [ | ||
| "pyopnsense==0.2.0" | ||
| ], | ||
| "dependencies": [], | ||
| "codeowners": ["@mtreinish"] | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| """Tests for the opnsense component.""" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| """The tests for the opnsense device tracker platform.""" | ||
|
|
||
| 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_API_KEY, CONF_URL, CONF_VERIFY_SSL | ||
| from homeassistant.setup import async_setup_component | ||
|
|
||
|
|
||
| @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, mocked_opnsense): | ||
| """Test creating an opnsense scanner.""" | ||
| 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" |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.