Add support for multiple AdGuard instances (#49116)

This commit is contained in:
Kevin Eifinger 2021-04-15 21:32:52 +02:00 committed by GitHub
parent 3d90d6073e
commit 5a01addd67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 98 additions and 64 deletions

View File

@ -10,7 +10,7 @@ import voluptuous as vol
from homeassistant.components.adguard.const import (
CONF_FORCE,
DATA_ADGUARD_CLIENT,
DATA_ADGUARD_VERION,
DATA_ADGUARD_VERSION,
DOMAIN,
SERVICE_ADD_URL,
SERVICE_DISABLE_URL,
@ -61,16 +61,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
session=session,
)
hass.data.setdefault(DOMAIN, {})[DATA_ADGUARD_CLIENT] = adguard
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = {DATA_ADGUARD_CLIENT: adguard}
try:
await adguard.version()
except AdGuardHomeConnectionError as exception:
raise ConfigEntryNotReady from exception
for platform in PLATFORMS:
for component in PLATFORMS:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, platform)
hass.config_entries.async_forward_entry_setup(entry, component)
)
async def add_url(call) -> None:
@ -126,8 +126,8 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.services.async_remove(DOMAIN, SERVICE_DISABLE_URL)
hass.services.async_remove(DOMAIN, SERVICE_REFRESH)
for platform in PLATFORMS:
await hass.config_entries.async_forward_entry_unload(entry, platform)
for component in PLATFORMS:
await hass.config_entries.async_forward_entry_unload(entry, component)
del hass.data[DOMAIN]
@ -138,13 +138,19 @@ class AdGuardHomeEntity(Entity):
"""Defines a base AdGuard Home entity."""
def __init__(
self, adguard, name: str, icon: str, enabled_default: bool = True
self,
adguard: AdGuardHome,
entry: ConfigEntry,
name: str,
icon: str,
enabled_default: bool = True,
) -> None:
"""Initialize the AdGuard Home entity."""
self._available = True
self._enabled_default = enabled_default
self._icon = icon
self._name = name
self._entry = entry
self.adguard = adguard
@property
@ -200,6 +206,8 @@ class AdGuardHomeDeviceEntity(AdGuardHomeEntity):
},
"name": "AdGuard Home",
"manufacturer": "AdGuard Team",
"sw_version": self.hass.data[DOMAIN].get(DATA_ADGUARD_VERION),
"sw_version": self.hass.data[DOMAIN][self._entry.entry_id].get(
DATA_ADGUARD_VERSION
),
"entry_type": "service",
}

View File

@ -63,12 +63,17 @@ class AdGuardHomeFlowHandler(ConfigFlow, domain=DOMAIN):
self, user_input: dict[str, Any] | None = None
) -> dict[str, Any]:
"""Handle a flow initiated by the user."""
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
if user_input is None:
return await self._show_setup_form(user_input)
entries = self._async_current_entries()
for entry in entries:
if (
entry.data[CONF_HOST] == user_input[CONF_HOST]
and entry.data[CONF_PORT] == user_input[CONF_PORT]
):
return self.async_abort(reason="single_instance_allowed")
errors = {}
session = async_get_clientsession(self.hass, user_input[CONF_VERIFY_SSL])

View File

@ -3,7 +3,7 @@
DOMAIN = "adguard"
DATA_ADGUARD_CLIENT = "adguard_client"
DATA_ADGUARD_VERION = "adguard_version"
DATA_ADGUARD_VERSION = "adguard_version"
CONF_FORCE = "force"

View File

@ -14,7 +14,7 @@ from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers.entity import Entity
from . import AdGuardHomeDeviceEntity
from .const import DATA_ADGUARD_CLIENT, DATA_ADGUARD_VERION, DOMAIN
from .const import DATA_ADGUARD_CLIENT, DATA_ADGUARD_VERSION, DOMAIN
SCAN_INTERVAL = timedelta(seconds=300)
PARALLEL_UPDATES = 4
@ -26,24 +26,24 @@ async def async_setup_entry(
async_add_entities: Callable[[list[Entity], bool], None],
) -> None:
"""Set up AdGuard Home sensor based on a config entry."""
adguard = hass.data[DOMAIN][DATA_ADGUARD_CLIENT]
adguard = hass.data[DOMAIN][entry.entry_id][DATA_ADGUARD_CLIENT]
try:
version = await adguard.version()
except AdGuardHomeConnectionError as exception:
raise PlatformNotReady from exception
hass.data[DOMAIN][DATA_ADGUARD_VERION] = version
hass.data[DOMAIN][entry.entry_id][DATA_ADGUARD_VERSION] = version
sensors = [
AdGuardHomeDNSQueriesSensor(adguard),
AdGuardHomeBlockedFilteringSensor(adguard),
AdGuardHomePercentageBlockedSensor(adguard),
AdGuardHomeReplacedParentalSensor(adguard),
AdGuardHomeReplacedSafeBrowsingSensor(adguard),
AdGuardHomeReplacedSafeSearchSensor(adguard),
AdGuardHomeAverageProcessingTimeSensor(adguard),
AdGuardHomeRulesCountSensor(adguard),
AdGuardHomeDNSQueriesSensor(adguard, entry),
AdGuardHomeBlockedFilteringSensor(adguard, entry),
AdGuardHomePercentageBlockedSensor(adguard, entry),
AdGuardHomeReplacedParentalSensor(adguard, entry),
AdGuardHomeReplacedSafeBrowsingSensor(adguard, entry),
AdGuardHomeReplacedSafeSearchSensor(adguard, entry),
AdGuardHomeAverageProcessingTimeSensor(adguard, entry),
AdGuardHomeRulesCountSensor(adguard, entry),
]
async_add_entities(sensors, True)
@ -55,6 +55,7 @@ class AdGuardHomeSensor(AdGuardHomeDeviceEntity, SensorEntity):
def __init__(
self,
adguard: AdGuardHome,
entry: ConfigEntry,
name: str,
icon: str,
measurement: str,
@ -66,7 +67,7 @@ class AdGuardHomeSensor(AdGuardHomeDeviceEntity, SensorEntity):
self._unit_of_measurement = unit_of_measurement
self.measurement = measurement
super().__init__(adguard, name, icon, enabled_default)
super().__init__(adguard, entry, name, icon, enabled_default)
@property
def unique_id(self) -> str:
@ -95,10 +96,15 @@ class AdGuardHomeSensor(AdGuardHomeDeviceEntity, SensorEntity):
class AdGuardHomeDNSQueriesSensor(AdGuardHomeSensor):
"""Defines a AdGuard Home DNS Queries sensor."""
def __init__(self, adguard: AdGuardHome) -> None:
def __init__(self, adguard: AdGuardHome, entry: ConfigEntry):
"""Initialize AdGuard Home sensor."""
super().__init__(
adguard, "AdGuard DNS Queries", "mdi:magnify", "dns_queries", "queries"
adguard,
entry,
"AdGuard DNS Queries",
"mdi:magnify",
"dns_queries",
"queries",
)
async def _adguard_update(self) -> None:
@ -109,10 +115,11 @@ class AdGuardHomeDNSQueriesSensor(AdGuardHomeSensor):
class AdGuardHomeBlockedFilteringSensor(AdGuardHomeSensor):
"""Defines a AdGuard Home blocked by filtering sensor."""
def __init__(self, adguard: AdGuardHome) -> None:
def __init__(self, adguard: AdGuardHome, entry: ConfigEntry):
"""Initialize AdGuard Home sensor."""
super().__init__(
adguard,
entry,
"AdGuard DNS Queries Blocked",
"mdi:magnify-close",
"blocked_filtering",
@ -128,10 +135,11 @@ class AdGuardHomeBlockedFilteringSensor(AdGuardHomeSensor):
class AdGuardHomePercentageBlockedSensor(AdGuardHomeSensor):
"""Defines a AdGuard Home blocked percentage sensor."""
def __init__(self, adguard: AdGuardHome) -> None:
def __init__(self, adguard: AdGuardHome, entry: ConfigEntry):
"""Initialize AdGuard Home sensor."""
super().__init__(
adguard,
entry,
"AdGuard DNS Queries Blocked Ratio",
"mdi:magnify-close",
"blocked_percentage",
@ -147,10 +155,11 @@ class AdGuardHomePercentageBlockedSensor(AdGuardHomeSensor):
class AdGuardHomeReplacedParentalSensor(AdGuardHomeSensor):
"""Defines a AdGuard Home replaced by parental control sensor."""
def __init__(self, adguard: AdGuardHome) -> None:
def __init__(self, adguard: AdGuardHome, entry: ConfigEntry):
"""Initialize AdGuard Home sensor."""
super().__init__(
adguard,
entry,
"AdGuard Parental Control Blocked",
"mdi:human-male-girl",
"blocked_parental",
@ -165,10 +174,11 @@ class AdGuardHomeReplacedParentalSensor(AdGuardHomeSensor):
class AdGuardHomeReplacedSafeBrowsingSensor(AdGuardHomeSensor):
"""Defines a AdGuard Home replaced by safe browsing sensor."""
def __init__(self, adguard: AdGuardHome) -> None:
def __init__(self, adguard: AdGuardHome, entry: ConfigEntry):
"""Initialize AdGuard Home sensor."""
super().__init__(
adguard,
entry,
"AdGuard Safe Browsing Blocked",
"mdi:shield-half-full",
"blocked_safebrowsing",
@ -183,10 +193,11 @@ class AdGuardHomeReplacedSafeBrowsingSensor(AdGuardHomeSensor):
class AdGuardHomeReplacedSafeSearchSensor(AdGuardHomeSensor):
"""Defines a AdGuard Home replaced by safe search sensor."""
def __init__(self, adguard: AdGuardHome) -> None:
def __init__(self, adguard: AdGuardHome, entry: ConfigEntry):
"""Initialize AdGuard Home sensor."""
super().__init__(
adguard,
entry,
"AdGuard Safe Searches Enforced",
"mdi:shield-search",
"enforced_safesearch",
@ -201,10 +212,11 @@ class AdGuardHomeReplacedSafeSearchSensor(AdGuardHomeSensor):
class AdGuardHomeAverageProcessingTimeSensor(AdGuardHomeSensor):
"""Defines a AdGuard Home average processing time sensor."""
def __init__(self, adguard: AdGuardHome) -> None:
def __init__(self, adguard: AdGuardHome, entry: ConfigEntry):
"""Initialize AdGuard Home sensor."""
super().__init__(
adguard,
entry,
"AdGuard Average Processing Speed",
"mdi:speedometer",
"average_speed",
@ -220,10 +232,11 @@ class AdGuardHomeAverageProcessingTimeSensor(AdGuardHomeSensor):
class AdGuardHomeRulesCountSensor(AdGuardHomeSensor):
"""Defines a AdGuard Home rules count sensor."""
def __init__(self, adguard: AdGuardHome) -> None:
def __init__(self, adguard: AdGuardHome, entry: ConfigEntry):
"""Initialize AdGuard Home sensor."""
super().__init__(
adguard,
entry,
"AdGuard Rules Count",
"mdi:counter",
"rules_count",

View File

@ -14,7 +14,7 @@ from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers.entity import Entity
from . import AdGuardHomeDeviceEntity
from .const import DATA_ADGUARD_CLIENT, DATA_ADGUARD_VERION, DOMAIN
from .const import DATA_ADGUARD_CLIENT, DATA_ADGUARD_VERSION, DOMAIN
_LOGGER = logging.getLogger(__name__)
@ -28,22 +28,22 @@ async def async_setup_entry(
async_add_entities: Callable[[list[Entity], bool], None],
) -> None:
"""Set up AdGuard Home switch based on a config entry."""
adguard = hass.data[DOMAIN][DATA_ADGUARD_CLIENT]
adguard = hass.data[DOMAIN][entry.entry_id][DATA_ADGUARD_CLIENT]
try:
version = await adguard.version()
except AdGuardHomeConnectionError as exception:
raise PlatformNotReady from exception
hass.data[DOMAIN][DATA_ADGUARD_VERION] = version
hass.data[DOMAIN][entry.entry_id][DATA_ADGUARD_VERSION] = version
switches = [
AdGuardHomeProtectionSwitch(adguard),
AdGuardHomeFilteringSwitch(adguard),
AdGuardHomeParentalSwitch(adguard),
AdGuardHomeSafeBrowsingSwitch(adguard),
AdGuardHomeSafeSearchSwitch(adguard),
AdGuardHomeQueryLogSwitch(adguard),
AdGuardHomeProtectionSwitch(adguard, entry),
AdGuardHomeFilteringSwitch(adguard, entry),
AdGuardHomeParentalSwitch(adguard, entry),
AdGuardHomeSafeBrowsingSwitch(adguard, entry),
AdGuardHomeSafeSearchSwitch(adguard, entry),
AdGuardHomeQueryLogSwitch(adguard, entry),
]
async_add_entities(switches, True)
@ -54,6 +54,7 @@ class AdGuardHomeSwitch(AdGuardHomeDeviceEntity, SwitchEntity):
def __init__(
self,
adguard: AdGuardHome,
entry: ConfigEntry,
name: str,
icon: str,
key: str,
@ -62,7 +63,7 @@ class AdGuardHomeSwitch(AdGuardHomeDeviceEntity, SwitchEntity):
"""Initialize AdGuard Home switch."""
self._state = False
self._key = key
super().__init__(adguard, name, icon, enabled_default)
super().__init__(adguard, entry, name, icon, enabled_default)
@property
def unique_id(self) -> str:
@ -104,10 +105,10 @@ class AdGuardHomeSwitch(AdGuardHomeDeviceEntity, SwitchEntity):
class AdGuardHomeProtectionSwitch(AdGuardHomeSwitch):
"""Defines a AdGuard Home protection switch."""
def __init__(self, adguard: AdGuardHome) -> None:
def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None:
"""Initialize AdGuard Home switch."""
super().__init__(
adguard, "AdGuard Protection", "mdi:shield-check", "protection"
adguard, entry, "AdGuard Protection", "mdi:shield-check", "protection"
)
async def _adguard_turn_off(self) -> None:
@ -126,10 +127,10 @@ class AdGuardHomeProtectionSwitch(AdGuardHomeSwitch):
class AdGuardHomeParentalSwitch(AdGuardHomeSwitch):
"""Defines a AdGuard Home parental control switch."""
def __init__(self, adguard: AdGuardHome) -> None:
def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None:
"""Initialize AdGuard Home switch."""
super().__init__(
adguard, "AdGuard Parental Control", "mdi:shield-check", "parental"
adguard, entry, "AdGuard Parental Control", "mdi:shield-check", "parental"
)
async def _adguard_turn_off(self) -> None:
@ -148,10 +149,10 @@ class AdGuardHomeParentalSwitch(AdGuardHomeSwitch):
class AdGuardHomeSafeSearchSwitch(AdGuardHomeSwitch):
"""Defines a AdGuard Home safe search switch."""
def __init__(self, adguard: AdGuardHome) -> None:
def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None:
"""Initialize AdGuard Home switch."""
super().__init__(
adguard, "AdGuard Safe Search", "mdi:shield-check", "safesearch"
adguard, entry, "AdGuard Safe Search", "mdi:shield-check", "safesearch"
)
async def _adguard_turn_off(self) -> None:
@ -170,10 +171,10 @@ class AdGuardHomeSafeSearchSwitch(AdGuardHomeSwitch):
class AdGuardHomeSafeBrowsingSwitch(AdGuardHomeSwitch):
"""Defines a AdGuard Home safe search switch."""
def __init__(self, adguard: AdGuardHome) -> None:
def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None:
"""Initialize AdGuard Home switch."""
super().__init__(
adguard, "AdGuard Safe Browsing", "mdi:shield-check", "safebrowsing"
adguard, entry, "AdGuard Safe Browsing", "mdi:shield-check", "safebrowsing"
)
async def _adguard_turn_off(self) -> None:
@ -192,9 +193,11 @@ class AdGuardHomeSafeBrowsingSwitch(AdGuardHomeSwitch):
class AdGuardHomeFilteringSwitch(AdGuardHomeSwitch):
"""Defines a AdGuard Home filtering switch."""
def __init__(self, adguard: AdGuardHome) -> None:
def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None:
"""Initialize AdGuard Home switch."""
super().__init__(adguard, "AdGuard Filtering", "mdi:shield-check", "filtering")
super().__init__(
adguard, entry, "AdGuard Filtering", "mdi:shield-check", "filtering"
)
async def _adguard_turn_off(self) -> None:
"""Turn off the switch."""
@ -212,10 +215,11 @@ class AdGuardHomeFilteringSwitch(AdGuardHomeSwitch):
class AdGuardHomeQueryLogSwitch(AdGuardHomeSwitch):
"""Defines a AdGuard Home query log switch."""
def __init__(self, adguard: AdGuardHome) -> None:
def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None:
"""Initialize AdGuard Home switch."""
super().__init__(
adguard,
entry,
"AdGuard Query Log",
"mdi:shield-check",
"querylog",

View File

@ -92,10 +92,14 @@ async def test_full_flow_implementation(
async def test_integration_already_exists(hass: HomeAssistant) -> None:
"""Test we only allow a single config flow."""
MockConfigEntry(domain=DOMAIN).add_to_hass(hass)
MockConfigEntry(
domain=DOMAIN, data={"host": "mock-adguard", "port": "3000"}
).add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": "user"}
DOMAIN,
data={"host": "mock-adguard", "port": "3000"},
context={"source": "user"},
)
assert result["type"] == "abort"
assert result["reason"] == "single_instance_allowed"
@ -104,11 +108,11 @@ async def test_integration_already_exists(hass: HomeAssistant) -> None:
async def test_hassio_single_instance(hass: HomeAssistant) -> None:
"""Test we only allow a single config flow."""
MockConfigEntry(
domain="adguard", data={"host": "mock-adguard", "port": "3000"}
domain=DOMAIN, data={"host": "mock-adguard", "port": "3000"}
).add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
"adguard",
DOMAIN,
data={"addon": "AdGuard Home Addon", "host": "mock-adguard", "port": "3000"},
context={"source": "hassio"},
)
@ -119,13 +123,13 @@ async def test_hassio_single_instance(hass: HomeAssistant) -> None:
async def test_hassio_update_instance_not_running(hass: HomeAssistant) -> None:
"""Test we only allow a single config flow."""
entry = MockConfigEntry(
domain="adguard", data={"host": "mock-adguard", "port": "3000"}
domain=DOMAIN, data={"host": "mock-adguard", "port": "3000"}
)
entry.add_to_hass(hass)
assert entry.state == config_entries.ENTRY_STATE_NOT_LOADED
result = await hass.config_entries.flow.async_init(
"adguard",
DOMAIN,
data={
"addon": "AdGuard Home Addon",
"host": "mock-adguard-updated",
@ -153,7 +157,7 @@ async def test_hassio_update_instance_running(
)
entry = MockConfigEntry(
domain="adguard",
domain=DOMAIN,
data={
"host": "mock-adguard",
"port": "3000",
@ -184,7 +188,7 @@ async def test_hassio_update_instance_running(
return_value=True,
) as mock_load:
result = await hass.config_entries.flow.async_init(
"adguard",
DOMAIN,
data={
"addon": "AdGuard Home Addon",
"host": "mock-adguard-updated",
@ -211,7 +215,7 @@ async def test_hassio_confirm(
)
result = await hass.config_entries.flow.async_init(
"adguard",
DOMAIN,
data={"addon": "AdGuard Home Addon", "host": "mock-adguard", "port": 3000},
context={"source": "hassio"},
)
@ -239,7 +243,7 @@ async def test_hassio_connection_error(
)
result = await hass.config_entries.flow.async_init(
"adguard",
DOMAIN,
data={"addon": "AdGuard Home Addon", "host": "mock-adguard", "port": 3000},
context={"source": "hassio"},
)