Skip to content

Commit

Permalink
Add configuration flow to Whois (#63069)
Browse files Browse the repository at this point in the history
Co-authored-by: Dave T <17680170+davet2001@users.noreply.github.com>
  • Loading branch information
frenck and davet2001 committed Dec 30, 2021
1 parent 9971831 commit b5300fb
Show file tree
Hide file tree
Showing 14 changed files with 255 additions and 14 deletions.
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -1268,6 +1268,7 @@ omit =
homeassistant/components/waze_travel_time/helpers.py
homeassistant/components/waze_travel_time/sensor.py
homeassistant/components/webostv/*
homeassistant/components/whois/__init__.py
homeassistant/components/whois/sensor.py
homeassistant/components/wiffi/*
homeassistant/components/wirelesstag/*
Expand Down
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -1035,6 +1035,7 @@ tests/components/wemo/* @esev
homeassistant/components/whirlpool/* @abmantis
tests/components/whirlpool/* @abmantis
homeassistant/components/whois/* @frenck
tests/components/whois/* @frenck
homeassistant/components/wiffi/* @mampfes
tests/components/wiffi/* @mampfes
homeassistant/components/wilight/* @leofig-rj
Expand Down
17 changes: 16 additions & 1 deletion homeassistant/components/whois/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,16 @@
"""The whois component."""
"""The Whois integration."""
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant

from .const import PLATFORMS


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up from a config entry."""
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
52 changes: 52 additions & 0 deletions homeassistant/components/whois/config_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""Config flow to configure the Whois integration."""
from __future__ import annotations

from typing import Any

import voluptuous as vol

from homeassistant.config_entries import ConfigFlow
from homeassistant.const import CONF_DOMAIN, CONF_NAME
from homeassistant.data_entry_flow import FlowResult

from .const import DOMAIN


class WhoisFlowHandler(ConfigFlow, domain=DOMAIN):
"""Config flow for Whois."""

VERSION = 1

imported_name: str | None = None

async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle a flow initialized by the user."""
if user_input is not None:
await self.async_set_unique_id(user_input[CONF_DOMAIN].lower())
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=self.imported_name or user_input[CONF_DOMAIN],
data={
CONF_DOMAIN: user_input[CONF_DOMAIN].lower(),
},
)

return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(CONF_DOMAIN): str,
}
),
)

async def async_step_import(self, config: dict[str, Any]) -> FlowResult:
"""Handle a flow initialized by importing a config."""
self.imported_name = config[CONF_NAME]
return await self.async_step_user(
user_input={
CONF_DOMAIN: config[CONF_DOMAIN],
}
)
3 changes: 3 additions & 0 deletions homeassistant/components/whois/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
import logging
from typing import Final

from homeassistant.const import Platform

DOMAIN: Final = "whois"
PLATFORMS = [Platform.SENSOR]

LOGGER = logging.getLogger(__package__)

Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/whois/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"name": "Whois",
"documentation": "https://www.home-assistant.io/integrations/whois",
"requirements": ["python-whois==0.7.3"],
"config_flow": true,
"codeowners": ["@frenck"],
"iot_class": "cloud_polling"
}
47 changes: 34 additions & 13 deletions homeassistant/components/whois/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import whois

from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.const import CONF_DOMAIN, CONF_NAME, TIME_DAYS
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
Expand All @@ -19,10 +20,11 @@
ATTR_REGISTRAR,
ATTR_UPDATED,
DEFAULT_NAME,
DOMAIN,
LOGGER,
)

SCANTERVAL = timedelta(hours=24)
SCAN_INTERVAL = timedelta(hours=24)

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
Expand All @@ -39,33 +41,52 @@ def setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the WHOIS sensor."""
domain = config[CONF_DOMAIN]
name = config[CONF_NAME]

LOGGER.warning(
"Configuration of the Whois platform in YAML is deprecated and will be "
"removed in Home Assistant 2022.4; Your existing configuration "
"has been imported into the UI automatically and can be safely removed "
"from your configuration.yaml file"
)
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_IMPORT},
data={CONF_DOMAIN: config[CONF_DOMAIN], CONF_NAME: config[CONF_NAME]},
)
)


async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the platform from config_entry."""
domain = entry.data[CONF_DOMAIN]
try:
if "expiration_date" in whois.whois(domain):
add_entities([WhoisSensor(name, domain)], True)
else:
LOGGER.error(
"WHOIS lookup for %s didn't contain an expiration date", domain
)
return
info = await hass.async_add_executor_job(whois.whois, domain)
except whois.BaseException as ex: # pylint: disable=broad-except
LOGGER.error("Exception %s occurred during WHOIS lookup for %s", ex, domain)
return

if "expiration_date" not in info:
LOGGER.error("WHOIS lookup for %s didn't contain an expiration date", domain)
return

async_add_entities([WhoisSensor(domain)], True)


class WhoisSensor(SensorEntity):
"""Implementation of a WHOIS sensor."""

_attr_icon = "mdi:calendar-clock"
_attr_native_unit_of_measurement = TIME_DAYS

def __init__(self, name: str, domain: str) -> None:
def __init__(self, domain: str) -> None:
"""Initialize the sensor."""
self._attr_name = domain
self.whois = whois.whois
self._domain = domain
self._attr_name = name

def _empty_value_and_attributes(self) -> None:
"""Empty the state and attributes on an error."""
Expand Down
14 changes: 14 additions & 0 deletions homeassistant/components/whois/strings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"config": {
"step": {
"user": {
"data": {
"domain": "Domain name"
}
}
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
}
}
}
14 changes: 14 additions & 0 deletions homeassistant/components/whois/translations/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"config": {
"abort": {
"already_configured": "Service is already configured"
},
"step": {
"user": {
"data": {
"domain": "Domain name"
}
}
}
}
}
1 change: 1 addition & 0 deletions homeassistant/generated/config_flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@
"waze_travel_time",
"wemo",
"whirlpool",
"whois",
"wiffi",
"wilight",
"withings",
Expand Down
3 changes: 3 additions & 0 deletions requirements_test_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1193,6 +1193,9 @@ python-tado==0.12.0
# homeassistant.components.twitch
python-twitch-client==0.6.0

# homeassistant.components.whois
python-whois==0.7.3

# homeassistant.components.awair
python_awair==0.2.1

Expand Down
1 change: 1 addition & 0 deletions tests/components/whois/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Tests for the Whois integration."""
34 changes: 34 additions & 0 deletions tests/components/whois/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""Fixtures for Whois integration tests."""
from __future__ import annotations

from collections.abc import Generator
from unittest.mock import AsyncMock, patch

import pytest

from homeassistant.components.whois.const import DOMAIN
from homeassistant.const import CONF_DOMAIN

from tests.common import MockConfigEntry


@pytest.fixture
def mock_config_entry() -> MockConfigEntry:
"""Return the default mocked config entry."""
return MockConfigEntry(
title="Home Assistant",
domain=DOMAIN,
data={
CONF_DOMAIN: "Home-Assistant.io",
},
unique_id="home-assistant.io",
)


@pytest.fixture
def mock_setup_entry() -> Generator[AsyncMock, None, None]:
"""Mock setting up a config entry."""
with patch(
"homeassistant.components.whois.async_setup_entry", return_value=True
) as mock_setup:
yield mock_setup
80 changes: 80 additions & 0 deletions tests/components/whois/test_config_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""Tests for the Whois config flow."""

from unittest.mock import AsyncMock

from homeassistant.components.whois.const import DOMAIN
from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER
from homeassistant.const import CONF_DOMAIN, CONF_NAME
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import (
RESULT_TYPE_ABORT,
RESULT_TYPE_CREATE_ENTRY,
RESULT_TYPE_FORM,
)

from tests.common import MockConfigEntry


async def test_full_user_flow(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
) -> None:
"""Test the full user configuration flow."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)

assert result.get("type") == RESULT_TYPE_FORM
assert result.get("step_id") == SOURCE_USER
assert "flow_id" in result

result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={CONF_DOMAIN: "Example.com"},
)

assert result2.get("type") == RESULT_TYPE_CREATE_ENTRY
assert result2.get("title") == "Example.com"
assert result2.get("data") == {CONF_DOMAIN: "example.com"}

assert len(mock_setup_entry.mock_calls) == 1


async def test_already_configured(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test we abort if already configured."""
mock_config_entry.add_to_hass(hass)

result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_USER},
data={CONF_DOMAIN: "HOME-Assistant.io"},
)

assert result.get("type") == RESULT_TYPE_ABORT
assert result.get("reason") == "already_configured"

assert len(mock_setup_entry.mock_calls) == 0


async def test_import_flow(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
) -> None:
"""Test the import configuration flow."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_IMPORT},
data={CONF_DOMAIN: "Example.com", CONF_NAME: "My Example Domain"},
)

assert result.get("type") == RESULT_TYPE_CREATE_ENTRY
assert result.get("title") == "My Example Domain"
assert result.get("data") == {
CONF_DOMAIN: "example.com",
}

assert len(mock_setup_entry.mock_calls) == 1

0 comments on commit b5300fb

Please sign in to comment.