Skip to content

Commit

Permalink
Show repair and shutdown integration if new device id detected (#327)
Browse files Browse the repository at this point in the history
* Show repair and shutdown if new device id detected

* Update README.md
  • Loading branch information
Snuffy2 authored Nov 25, 2024
1 parent 83e9246 commit 8e34e9f
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 24 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ A Discord server to discuss the integration is available, please click the Disco
- [Switch](#switch)
- [Services](#services)
- [Known Issues](#known-issues)
- [Hardware Changes](#hardware-changes)
- [AdGuardHome](#adguardhome)

# Installation
Expand Down Expand Up @@ -223,11 +224,21 @@ data:
vouchergroup: Home Assistant
voucher_server: Voucher Server # Only needed if more than 1 Voucher Server
# Returns the vouchers as action response data
action: opnsense.toggle_alias
data:
alias: "iphones"
toggle_on_off: "toggle"
```
### [How to use `action response data` in an HA script or automation](https://www.home-assistant.io/docs/scripts/perform-actions/#use-templates-to-handle-response-data)

# Known Issues

## Hardware Changes

If you partially or fully change the OPNsense Hardware, it will require a removal and reinstall of this integration. This is to ensure changed interfaces, services, gateways, etc. are accounted for and don't leave duplicate or non-functioning entities.

## AdGuardHome

As mentioned [here](https://github.com/travisghansen/hass-opnsense/issues/22) using AdGuardHome can lead to problems with the plugin. Setting the Ratelimit in AdGuardHome to 0 will resolve this problem.
51 changes: 33 additions & 18 deletions custom_components/opnsense/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,10 @@
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import (
config_validation as cv,
)
from homeassistant.helpers import (
device_registry as dr,
)
from homeassistant.helpers import (
entity_registry as er,
)
from homeassistant.helpers import (
issue_registry as ir,
)
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers import issue_registry as ir
from homeassistant.helpers.aiohttp_client import async_create_clientsession
from homeassistant.helpers.typing import ConfigType
from homeassistant.helpers.update_coordinator import CoordinatorEntity
Expand Down Expand Up @@ -89,7 +81,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
device_tracker_enabled: bool = options.get(
CONF_DEVICE_TRACKER_ENABLED, DEFAULT_DEVICE_TRACKER_ENABLED
)
device_unique_id = config[CONF_DEVICE_UNIQUE_ID]
config_device_id: str = config[CONF_DEVICE_UNIQUE_ID]

client = OPNsenseClient(
url=url,
Expand All @@ -108,12 +100,33 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
name=f"{entry.title} state",
update_interval=timedelta(seconds=scan_interval),
client=client,
device_unique_id=device_unique_id,
device_unique_id=config_device_id,
)

await coordinator.async_config_entry_first_refresh()
# Trigger repair task and shutdown if device id has changed
router_device_id: str = await client.get_device_unique_id()
_LOGGER.debug(
f"[init async_setup_entry]: config device id: {config_device_id}, router device id: {router_device_id}"
)
if router_device_id != config_device_id and router_device_id:
ir.async_create_issue(
hass=hass,
domain=DOMAIN,
issue_id=f"{config_device_id}_device_id_mismatched",
is_fixable=False,
is_persistent=False,
severity=ir.IssueSeverity.ERROR,
translation_key="device_id_mismatched",
)
_LOGGER.error(
"OPNsense Device ID has changed which indicates new or changed hardware. "
"In order to accomodate this, hass-opnsense needs to be removed and reinstalled for this router. "
"hass-opnsense is shutting down."
)
await coordinator.async_shutdown()
return False

firmware: str | None = coordinator.data.get("host_firmware_version", None)
firmware: str | None = await client.get_host_firmware_version()
_LOGGER.info(f"OPNsense Firmware {firmware}")
try:
if awesomeversion.AwesomeVersion(firmware) < awesomeversion.AwesomeVersion(
Expand Down Expand Up @@ -169,6 +182,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
_LOGGER.warning("Unable to confirm OPNsense Firmware version")
pass

await coordinator.async_config_entry_first_refresh()

platforms: list = PLATFORMS.copy()
device_tracker_coordinator = None
if not device_tracker_enabled and "device_tracker" in platforms:
Expand All @@ -183,7 +198,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
name=f"{entry.title} Device Tracker state",
update_interval=timedelta(seconds=device_tracker_scan_interval),
client=client,
device_unique_id=device_unique_id,
device_unique_id=config_device_id,
device_tracker_coordinator=True,
)

Expand All @@ -196,7 +211,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
OPNSENSE_CLIENT: client,
UNDO_UPDATE_LISTENER: [undo_listener],
LOADED_PLATFORMS: platforms,
CONF_DEVICE_UNIQUE_ID: device_unique_id,
CONF_DEVICE_UNIQUE_ID: config_device_id,
}

if device_tracker_enabled:
Expand Down
34 changes: 30 additions & 4 deletions custom_components/opnsense/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
from typing import Any

from homeassistant.core import HomeAssistant
from homeassistant.helpers import issue_registry as ir
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator

from .const import ATTR_UNBOUND_BLOCKLIST
from .const import ATTR_UNBOUND_BLOCKLIST, DOMAIN
from .helpers import dict_get
from .pyopnsense import OPNsenseClient

Expand All @@ -33,6 +34,7 @@ def __init__(
self._client: OPNsenseClient = client
self._state: Mapping[str, Any] = {}
self._device_tracker_coordinator: bool = device_tracker_coordinator
self._mismatched_count = 0
self._device_unique_id: str = device_unique_id
super().__init__(
hass,
Expand Down Expand Up @@ -146,11 +148,35 @@ async def _async_update_data(self) -> Mapping[str, Any]:
)
return {}
if self._state.get("device_unique_id") != self._device_unique_id:
_LOGGER.error(
f"Coordinator error. OPNsense Router Device ID ({self._state.get('device_unique_id')}) differs from the one saved in hass-opnsense ({self._device_unique_id})"
_LOGGER.debug(
f"[Coordinator async_update_data]: config device id: {self._device_unique_id}, "
f"router device id: {self._state.get('device_unique_id')}"
)
# Create repair task here
if self._state.get("device_unique_id"):
_LOGGER.error(
f"Coordinator error. OPNsense Router Device ID ({self._state.get('device_unique_id')}) differs from the one saved in hass-opnsense ({self._device_unique_id})"
)
self._mismatched_count += 1
# Trigger repair task and shutdown if this happens 3 times in a row
if self._mismatched_count == 3:
ir.async_create_issue(
hass=self.hass,
domain=DOMAIN,
issue_id=f"{self._device_unique_id}_device_id_mismatched",
is_fixable=False,
is_persistent=False,
severity=ir.IssueSeverity.ERROR,
translation_key="device_id_mismatched",
)
_LOGGER.error(
"OPNsense Device ID has changed which indicates new or changed hardware. "
"In order to accomodate this, hass-opnsense needs to be removed and reinstalled for this router. "
"hass-opnsense is shutting down."
)
await self.async_shutdown()
return {}
else:
self._mismatched_count = 0

# calculate pps and kbps
update_time = dict_get(self._state, "update_time")
Expand Down
9 changes: 7 additions & 2 deletions custom_components/opnsense/pyopnsense/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ async def _filter_configure(self) -> None:

@_log_errors
async def get_device_unique_id(self) -> str | None:
instances: Mapping[str, Any] | list = await self._post(
instances: Mapping[str, Any] | list = await self._get(
"/api/interfaces/overview/export"
)
if not isinstance(instances, list):
Expand All @@ -429,7 +429,12 @@ async def get_device_unique_id(self) -> str | None:
unique_mac_addresses[0] if unique_mac_addresses else None
)
if device_unique_id:
return device_unique_id.replace(":", "_").strip()
device_unique_id_fmt = device_unique_id.replace(":", "_").strip()
_LOGGER.debug(
f"[get_device_unique_id] device_unique_id: {device_unique_id_fmt}"
)
return device_unique_id_fmt
_LOGGER.debug("[get_device_unique_id] device_unique_id: None")
return None

@_log_errors
Expand Down
4 changes: 4 additions & 0 deletions custom_components/opnsense/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@
"below_ltd_firmware": {
"title": "OPNsense Firmware below Recommended Version",
"description": "Some hass-opnsense {version} functions require OPNsense firmware {ltd_firmware} or later. With firmware {firmware}, some functions may not work and there may be errors in the logs."
},
"device_id_mismatched": {
"title": "OPNsense Hardware Has Changed",
"description": "OPNsense Device ID has changed which indicates new or changed hardware. In order to accomodate this, hass-opnsense needs to be removed and reinstalled for this router. hass-opnsense is shutting down."
}
},
"title": "OPNsense"
Expand Down

0 comments on commit 8e34e9f

Please sign in to comment.