Initial commit
Some checks failed
Tests / test (3.13) (push) Failing after 23s
Tests / lint (push) Failing after 20s
Tests / hacs (push) Failing after 52s

Signed-off-by: Rafal Zielinski <sq4ind@gmail.com>
This commit is contained in:
2025-10-02 16:00:15 +01:00
commit d4cdcc04c0
25 changed files with 2650 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
"""The AdGuard Control Hub integration."""
from __future__ import annotations
import logging
from datetime import timedelta
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN
from .coordinator import AdGuardDataUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
PLATFORMS: list[Platform] = [
Platform.SWITCH,
Platform.SENSOR,
Platform.BINARY_SENSOR,
]
SCAN_INTERVAL = timedelta(seconds=30)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up AdGuard Control Hub from a config entry."""
session = async_get_clientsession(hass)
coordinator = AdGuardDataUpdateCoordinator(
hass,
session,
entry.data,
SCAN_INTERVAL,
)
# Test connection
await coordinator.async_config_entry_first_refresh()
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = coordinator
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok
async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Reload config entry."""
await async_unload_entry(hass, entry)
await async_setup_entry(hass, entry)

View File

@@ -0,0 +1,154 @@
"""AdGuard Home API client."""
from __future__ import annotations
import asyncio
import logging
from typing import Any
import aiohttp
import async_timeout
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import (
API_CLIENTS,
API_DNS_CONFIG,
API_STATUS,
API_STATS,
)
_LOGGER = logging.getLogger(__name__)
class AdGuardHomeApiError(Exception):
"""Exception to indicate a general API error."""
class AdGuardHomeConnectionError(AdGuardHomeApiError):
"""Exception to indicate a connection error."""
class AdGuardHomeAuthError(AdGuardHomeApiError):
"""Exception to indicate an authentication error."""
class AdGuardHomeAPI:
"""AdGuard Home API client."""
def __init__(
self,
session: aiohttp.ClientSession,
host: str,
port: int,
username: str | None = None,
password: str | None = None,
ssl: bool = False,
verify_ssl: bool = True,
) -> None:
"""Initialize the API client."""
self._session = session
self._host = host
self._port = port
self._username = username
self._password = password
self._ssl = ssl
self._verify_ssl = verify_ssl
protocol = "https" if ssl else "http"
self._base_url = f"{protocol}://{host}:{port}"
async def _request(
self,
method: str,
endpoint: str,
data: dict[str, Any] | None = None,
) -> dict[str, Any]:
"""Make a request to the AdGuard Home API."""
url = f"{self._base_url}{endpoint}"
auth = None
if self._username and self._password:
auth = aiohttp.BasicAuth(self._username, self._password)
headers = {"Content-Type": "application/json"}
try:
async with async_timeout.timeout(10):
async with self._session.request(
method,
url,
json=data,
auth=auth,
headers=headers,
ssl=self._verify_ssl,
) as response:
if response.status == 401:
raise AdGuardHomeAuthError("Authentication failed")
if response.status == 403:
raise AdGuardHomeAuthError("Access forbidden")
if response.status not in (200, 204):
text = await response.text()
raise AdGuardHomeApiError(
f"Request failed with status {response.status}: {text}"
)
if response.status == 204:
return {}
return await response.json()
except asyncio.TimeoutError as err:
raise AdGuardHomeConnectionError("Timeout connecting to AdGuard Home") from err
except aiohttp.ClientError as err:
raise AdGuardHomeConnectionError(f"Error connecting to AdGuard Home: {err}") from err
async def get_status(self) -> dict[str, Any]:
"""Get AdGuard Home status."""
return await self._request("GET", API_STATUS)
async def get_stats(self) -> dict[str, Any]:
"""Get AdGuard Home statistics."""
return await self._request("GET", API_STATS)
async def get_clients(self) -> dict[str, Any]:
"""Get AdGuard Home clients."""
return await self._request("GET", API_CLIENTS)
async def set_protection(self, enabled: bool) -> None:
"""Enable or disable protection."""
data = {"protection_enabled": enabled}
await self._request("POST", API_DNS_CONFIG, data)
async def set_filtering(self, enabled: bool) -> None:
"""Enable or disable filtering."""
data = {"filtering_enabled": enabled}
await self._request("POST", API_DNS_CONFIG, data)
async def set_safebrowsing(self, enabled: bool) -> None:
"""Enable or disable safe browsing."""
data = {"safebrowsing_enabled": enabled}
await self._request("POST", API_DNS_CONFIG, data)
async def set_parental_control(self, enabled: bool) -> None:
"""Enable or disable parental control."""
data = {"parental_enabled": enabled}
await self._request("POST", API_DNS_CONFIG, data)
async def set_safe_search(self, enabled: bool) -> None:
"""Enable or disable safe search."""
data = {"safesearch_enabled": enabled}
await self._request("POST", API_DNS_CONFIG, data)
async def set_query_log(self, enabled: bool) -> None:
"""Enable or disable query log."""
data = {"querylog_enabled": enabled}
await self._request("POST", API_DNS_CONFIG, data)
async def test_connection(self) -> bool:
"""Test connection to AdGuard Home."""
try:
await self.get_status()
return True
except AdGuardHomeApiError:
return False

View File

@@ -0,0 +1,80 @@
"""AdGuard Control Hub binary sensor platform."""
from __future__ import annotations
import logging
from typing import Any
from homeassistant.components.binary_sensor import BinarySensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN
from .coordinator import AdGuardDataUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up AdGuard Control Hub binary sensor based on a config entry."""
coordinator = hass.data[DOMAIN][entry.entry_id]
entities = [
AdGuardBinarySensor(
coordinator,
entry,
"running",
"AdGuard Home Running",
"mdi:shield-check-outline",
),
]
async_add_entities(entities)
class AdGuardBinarySensor(CoordinatorEntity[AdGuardDataUpdateCoordinator], BinarySensorEntity):
"""Representation of an AdGuard Control Hub binary sensor."""
def __init__(
self,
coordinator: AdGuardDataUpdateCoordinator,
entry: ConfigEntry,
sensor_type: str,
name: str,
icon: str,
) -> None:
"""Initialize the binary sensor."""
super().__init__(coordinator)
self._sensor_type = sensor_type
self._entry = entry
self._attr_name = name
self._attr_icon = icon
self._attr_unique_id = f"{entry.entry_id}_{sensor_type}"
@property
def device_info(self) -> DeviceInfo:
"""Return device information about this AdGuard Home instance."""
return DeviceInfo(
identifiers={(DOMAIN, self._entry.entry_id)},
name="AdGuard Control Hub",
manufacturer="AdGuard",
model="AdGuard Home",
sw_version=self.coordinator.data.get("status", {}).get("version"),
configuration_url=f"http://{self._entry.data['host']}:{self._entry.data['port']}",
)
@property
def is_on(self) -> bool | None:
"""Return True if the binary sensor is on."""
if self.coordinator.data is None:
return None
# If we can fetch data, AdGuard Home is running
return self.coordinator.data.get("status") is not None

View File

@@ -0,0 +1,100 @@
"""Config flow for AdGuard Control Hub integration."""
from __future__ import annotations
import logging
from typing import Any
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResult
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .api import AdGuardHomeAPI, AdGuardHomeApiError, AdGuardHomeAuthError, AdGuardHomeConnectionError
from .const import CONF_SSL, CONF_VERIFY_SSL, DEFAULT_NAME, DEFAULT_PORT, DEFAULT_SSL, DEFAULT_VERIFY_SSL, DOMAIN
_LOGGER = logging.getLogger(__name__)
STEP_USER_DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_HOST): str,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): int,
vol.Optional(CONF_USERNAME): str,
vol.Optional(CONF_PASSWORD): str,
vol.Optional(CONF_SSL, default=DEFAULT_SSL): bool,
vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): bool,
}
)
async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, Any]:
"""Validate the user input allows us to connect."""
session = async_get_clientsession(hass)
api = AdGuardHomeAPI(
session,
data[CONF_HOST],
data[CONF_PORT],
data.get(CONF_USERNAME),
data.get(CONF_PASSWORD),
data.get(CONF_SSL, DEFAULT_SSL),
data.get(CONF_VERIFY_SSL, DEFAULT_VERIFY_SSL),
)
try:
status = await api.get_status()
return {"title": f"AdGuard Home ({data[CONF_HOST]})", "version": status.get("version", "Unknown")}
except AdGuardHomeConnectionError as err:
raise CannotConnect from err
except AdGuardHomeAuthError as err:
raise InvalidAuth from err
except AdGuardHomeApiError as err:
raise CannotConnect from err
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for AdGuard Control Hub."""
VERSION = 1
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle the initial step."""
if user_input is None:
return self.async_show_form(
step_id="user", data_schema=STEP_USER_DATA_SCHEMA
)
errors = {}
try:
info = await validate_input(self.hass, user_input)
except CannotConnect:
errors["base"] = "cannot_connect"
except InvalidAuth:
errors["base"] = "invalid_auth"
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
else:
# Create unique ID from host and port
unique_id = f"{user_input[CONF_HOST]}:{user_input[CONF_PORT]}"
await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_configured()
return self.async_create_entry(title=info["title"], data=user_input)
return self.async_show_form(
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
)
class CannotConnect(HomeAssistantError):
"""Error to indicate we cannot connect."""
class InvalidAuth(HomeAssistantError):
"""Error to indicate there is invalid auth."""

View File

@@ -0,0 +1,108 @@
"""Constants for the AdGuard Control Hub integration."""
from homeassistant.const import Platform
DOMAIN = "adguard_control_hub"
DEFAULT_NAME = "AdGuard Control Hub"
DEFAULT_PORT = 3000
DEFAULT_SSL = False
DEFAULT_VERIFY_SSL = True
# Configuration
CONF_HOST = "host"
CONF_PORT = "port"
CONF_USERNAME = "username"
CONF_PASSWORD = "password"
CONF_SSL = "ssl"
CONF_VERIFY_SSL = "verify_ssl"
# AdGuard API endpoints
API_STATUS = "/control/status"
API_DNS_CONFIG = "/control/dns_config"
API_STATS = "/control/stats"
API_CLIENTS = "/control/clients"
API_REWRITE = "/control/rewrite"
API_FILTERING = "/control/filtering"
# Switch types
SWITCH_PROTECTION = "protection"
SWITCH_FILTERING = "filtering"
SWITCH_SAFEBROWSING = "safebrowsing"
SWITCH_PARENTAL = "parental"
SWITCH_SAFESEARCH = "safesearch"
SWITCH_QUERY_LOG = "query_log"
SWITCHES = {
SWITCH_PROTECTION: {
"name": "AdGuard Protection",
"icon": "mdi:shield-check",
"api_key": "protection_enabled",
},
SWITCH_FILTERING: {
"name": "DNS Filtering",
"icon": "mdi:filter",
"api_key": "filtering_enabled",
},
SWITCH_SAFEBROWSING: {
"name": "Safe Browsing",
"icon": "mdi:shield-bug",
"api_key": "safebrowsing_enabled",
},
SWITCH_PARENTAL: {
"name": "Parental Control",
"icon": "mdi:account-child-circle",
"api_key": "parental_enabled",
},
SWITCH_SAFESEARCH: {
"name": "Safe Search",
"icon": "mdi:shield-search",
"api_key": "safesearch_enabled",
},
SWITCH_QUERY_LOG: {
"name": "Query Log",
"icon": "mdi:file-document-multiple",
"api_key": "querylog_enabled",
},
}
# Sensor types
SENSOR_DNS_QUERIES = "dns_queries"
SENSOR_BLOCKED_QUERIES = "blocked_queries"
SENSOR_BLOCKED_PERCENTAGE = "blocked_percentage"
SENSOR_ACTIVE_FILTERS = "active_filters"
SENSOR_AVG_PROCESSING_TIME = "avg_processing_time"
SENSORS = {
SENSOR_DNS_QUERIES: {
"name": "DNS Queries",
"icon": "mdi:dns",
"unit": "queries",
"api_key": "num_dns_queries",
},
SENSOR_BLOCKED_QUERIES: {
"name": "Blocked Queries",
"icon": "mdi:shield-check",
"unit": "queries",
"api_key": "num_blocked_filtering",
},
SENSOR_BLOCKED_PERCENTAGE: {
"name": "Blocked Percentage",
"icon": "mdi:percent",
"unit": "%",
"api_key": "blocked_percentage",
},
SENSOR_ACTIVE_FILTERS: {
"name": "Active Filter Rules",
"icon": "mdi:filter-check",
"unit": "rules",
"api_key": "num_replaced_safebrowsing",
},
SENSOR_AVG_PROCESSING_TIME: {
"name": "Average Processing Time",
"icon": "mdi:clock-fast",
"unit": "ms",
"api_key": "avg_processing_time",
},
}
# Update interval
DEFAULT_SCAN_INTERVAL = 30

View File

@@ -0,0 +1,100 @@
"""AdGuard Control Hub data update coordinator."""
from __future__ import annotations
import logging
from datetime import timedelta
from typing import Any
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .api import AdGuardHomeAPI, AdGuardHomeApiError, AdGuardHomeAuthError, AdGuardHomeConnectionError
from .const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_SSL, CONF_USERNAME, CONF_VERIFY_SSL, DOMAIN
_LOGGER = logging.getLogger(__name__)
class AdGuardDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""Class to manage fetching AdGuard Home data from the API."""
def __init__(
self,
hass: HomeAssistant,
session,
config: dict[str, Any],
update_interval: timedelta,
) -> None:
"""Initialize the data update coordinator."""
super().__init__(
hass,
_LOGGER,
name=DOMAIN,
update_interval=update_interval,
)
self.api = AdGuardHomeAPI(
session,
config[CONF_HOST],
config[CONF_PORT],
config.get(CONF_USERNAME),
config.get(CONF_PASSWORD),
config.get(CONF_SSL, False),
config.get(CONF_VERIFY_SSL, True),
)
self._config = config
async def _async_update_data(self) -> dict[str, Any]:
"""Update data via library."""
try:
# Get status and stats data
status_data = await self.api.get_status()
stats_data = await self.api.get_stats()
clients_data = await self.api.get_clients()
# Combine all data
data = {
"status": status_data,
"stats": stats_data,
"clients": clients_data,
}
# Calculate blocked percentage
if "stats" in data and "num_dns_queries" in data["stats"]:
queries = data["stats"].get("num_dns_queries", 0)
blocked = data["stats"].get("num_blocked_filtering", 0)
if queries > 0:
data["stats"]["blocked_percentage"] = round((blocked / queries) * 100, 2)
else:
data["stats"]["blocked_percentage"] = 0.0
return data
except AdGuardHomeAuthError as err:
raise ConfigEntryAuthFailed("Authentication failed") from err
except (AdGuardHomeConnectionError, AdGuardHomeApiError) as err:
raise UpdateFailed(f"Error communicating with AdGuard Home: {err}") from err
async def async_set_switch(self, switch_type: str, state: bool) -> None:
"""Set switch state."""
try:
if switch_type == "protection":
await self.api.set_protection(state)
elif switch_type == "filtering":
await self.api.set_filtering(state)
elif switch_type == "safebrowsing":
await self.api.set_safebrowsing(state)
elif switch_type == "parental":
await self.api.set_parental_control(state)
elif switch_type == "safesearch":
await self.api.set_safe_search(state)
elif switch_type == "query_log":
await self.api.set_query_log(state)
# Refresh data after changing state
await self.async_request_refresh()
except (AdGuardHomeConnectionError, AdGuardHomeApiError) as err:
raise UpdateFailed(f"Error setting switch {switch_type}: {err}") from err

View File

@@ -0,0 +1,16 @@
{
"domain": "adguard_control_hub",
"name": "AdGuard Control Hub",
"version": "1.0.0",
"documentation": "https://github.com/your-username/adguard-control-hub",
"issue_tracker": "https://github.com/your-username/adguard-control-hub/issues",
"dependencies": [],
"codeowners": [
"@your-username"
],
"requirements": [
"aiohttp>=3.8.0"
],
"config_flow": true,
"iot_class": "local_polling"
}

View File

@@ -0,0 +1,98 @@
"""AdGuard Control Hub sensor platform."""
from __future__ import annotations
import logging
from typing import Any
from homeassistant.components.sensor import SensorEntity, SensorStateClass
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN, SENSORS
from .coordinator import AdGuardDataUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up AdGuard Control Hub sensor based on a config entry."""
coordinator = hass.data[DOMAIN][entry.entry_id]
entities = []
for sensor_type, sensor_info in SENSORS.items():
entities.append(
AdGuardSensor(
coordinator,
entry,
sensor_type,
sensor_info,
)
)
async_add_entities(entities)
class AdGuardSensor(CoordinatorEntity[AdGuardDataUpdateCoordinator], SensorEntity):
"""Representation of an AdGuard Control Hub sensor."""
def __init__(
self,
coordinator: AdGuardDataUpdateCoordinator,
entry: ConfigEntry,
sensor_type: str,
sensor_info: dict[str, Any],
) -> None:
"""Initialize the sensor."""
super().__init__(coordinator)
self._sensor_type = sensor_type
self._sensor_info = sensor_info
self._entry = entry
self._attr_name = sensor_info["name"]
self._attr_icon = sensor_info["icon"]
self._attr_native_unit_of_measurement = sensor_info.get("unit")
self._attr_unique_id = f"{entry.entry_id}_{sensor_type}"
# Set state class for numeric sensors
if sensor_info.get("unit") in ["queries", "rules", "ms"]:
self._attr_state_class = SensorStateClass.MEASUREMENT
@property
def device_info(self) -> DeviceInfo:
"""Return device information about this AdGuard Home instance."""
return DeviceInfo(
identifiers={(DOMAIN, self._entry.entry_id)},
name="AdGuard Control Hub",
manufacturer="AdGuard",
model="AdGuard Home",
sw_version=self.coordinator.data.get("status", {}).get("version"),
configuration_url=f"http://{self._entry.data['host']}:{self._entry.data['port']}",
)
@property
def native_value(self) -> str | int | float | None:
"""Return the native value of the sensor."""
if self.coordinator.data is None:
return None
stats_data = self.coordinator.data.get("stats", {})
api_key = self._sensor_info["api_key"]
value = stats_data.get(api_key)
# Handle special cases
if self._sensor_type == "blocked_percentage":
return value
elif self._sensor_type == "avg_processing_time":
# Convert to milliseconds if needed
if value is not None:
return round(value, 2)
return value

View File

@@ -0,0 +1,26 @@
{
"config": {
"step": {
"user": {
"title": "AdGuard Control Hub Setup",
"description": "Configure connection to your AdGuard Home instance",
"data": {
"host": "Host",
"port": "Port",
"username": "Username (optional)",
"password": "Password (optional)",
"ssl": "Use HTTPS",
"verify_ssl": "Verify SSL certificate"
}
}
},
"error": {
"cannot_connect": "Failed to connect to AdGuard Home",
"invalid_auth": "Invalid authentication",
"unknown": "Unexpected error occurred"
},
"abort": {
"already_configured": "AdGuard Home instance already configured"
}
}
}

View File

@@ -0,0 +1,91 @@
"""AdGuard Control Hub switch platform."""
from __future__ import annotations
import logging
from typing import Any
from homeassistant.components.switch import SwitchEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN, SWITCHES
from .coordinator import AdGuardDataUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up AdGuard Control Hub switch based on a config entry."""
coordinator = hass.data[DOMAIN][entry.entry_id]
entities = []
for switch_type, switch_info in SWITCHES.items():
entities.append(
AdGuardSwitch(
coordinator,
entry,
switch_type,
switch_info,
)
)
async_add_entities(entities)
class AdGuardSwitch(CoordinatorEntity[AdGuardDataUpdateCoordinator], SwitchEntity):
"""Representation of an AdGuard Control Hub switch."""
def __init__(
self,
coordinator: AdGuardDataUpdateCoordinator,
entry: ConfigEntry,
switch_type: str,
switch_info: dict[str, Any],
) -> None:
"""Initialize the switch."""
super().__init__(coordinator)
self._switch_type = switch_type
self._switch_info = switch_info
self._entry = entry
self._attr_name = switch_info["name"]
self._attr_icon = switch_info["icon"]
self._attr_unique_id = f"{entry.entry_id}_{switch_type}"
@property
def device_info(self) -> DeviceInfo:
"""Return device information about this AdGuard Home instance."""
return DeviceInfo(
identifiers={(DOMAIN, self._entry.entry_id)},
name="AdGuard Control Hub",
manufacturer="AdGuard",
model="AdGuard Home",
sw_version=self.coordinator.data.get("status", {}).get("version"),
configuration_url=f"http://{self._entry.data['host']}:{self._entry.data['port']}",
)
@property
def is_on(self) -> bool | None:
"""Return True if entity is on."""
if self.coordinator.data is None:
return None
status_data = self.coordinator.data.get("status", {})
api_key = self._switch_info["api_key"]
return status_data.get(api_key, False)
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the entity on."""
await self.coordinator.async_set_switch(self._switch_type, True)
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the entity off."""
await self.coordinator.async_set_switch(self._switch_type, False)

View File

@@ -0,0 +1,36 @@
{
"config": {
"step": {
"user": {
"title": "AdGuard Control Hub Setup",
"description": "Configure connection to your AdGuard Home instance",
"data": {
"host": "Host",
"port": "Port",
"username": "Username (optional)",
"password": "Password (optional)",
"ssl": "Use HTTPS",
"verify_ssl": "Verify SSL certificate"
}
}
},
"error": {
"cannot_connect": "Failed to connect to AdGuard Home",
"invalid_auth": "Invalid authentication",
"unknown": "Unexpected error occurred"
},
"abort": {
"already_configured": "AdGuard Home instance already configured"
}
},
"options": {
"step": {
"init": {
"title": "AdGuard Control Hub Options",
"data": {
"scan_interval": "Update interval (seconds)"
}
}
}
}
}