Add config flow for zodiac (#95447)

* Add config flow for zodiac

* Add config flow for zodiac

* Fix feedback
This commit is contained in:
Joost Lekkerkerker 2023-06-30 12:58:07 +02:00 committed by GitHub
parent abf6e0e44d
commit 4ac92d755e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 177 additions and 17 deletions

View file

@ -1,9 +1,10 @@
"""The zodiac component."""
import voluptuous as vol
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.discovery import async_load_platform
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.helpers.typing import ConfigType
from .const import DOMAIN
@ -16,8 +17,32 @@ CONFIG_SCHEMA = vol.Schema(
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the zodiac component."""
async_create_issue(
hass,
DOMAIN,
"deprecated_yaml",
breaks_in_ha_version="2024.1.0",
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="deprecated_yaml",
)
hass.async_create_task(
async_load_platform(hass, Platform.SENSOR, DOMAIN, {}, config)
hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_IMPORT}, data=config
)
)
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Load a config entry."""
await hass.config_entries.async_forward_entry_setups(entry, [Platform.SENSOR])
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, [Platform.SENSOR])

View file

@ -0,0 +1,31 @@
"""Config flow to configure the Zodiac integration."""
from __future__ import annotations
from typing import Any
from homeassistant.config_entries import ConfigFlow
from homeassistant.data_entry_flow import FlowResult
from .const import DEFAULT_NAME, DOMAIN
class ZodiacConfigFlow(ConfigFlow, domain=DOMAIN):
"""Config flow for Zodiac."""
VERSION = 1
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle a flow initialized by the user."""
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
if user_input is not None:
return self.async_create_entry(title=DEFAULT_NAME, data={})
return self.async_show_form(step_id="user")
async def async_step_import(self, user_input: dict[str, Any]) -> FlowResult:
"""Handle import from configuration.yaml."""
return await self.async_step_user(user_input)

View file

@ -1,5 +1,6 @@
"""Constants for Zodiac."""
DOMAIN = "zodiac"
DEFAULT_NAME = "Zodiac"
# Signs
SIGN_ARIES = "aries"

View file

@ -2,7 +2,8 @@
"domain": "zodiac",
"name": "Zodiac",
"codeowners": ["@JulienTant"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/zodiac",
"iot_class": "local_polling",
"iot_class": "calculated",
"quality_scale": "silver"
}

View file

@ -2,14 +2,17 @@
from __future__ import annotations
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntryType
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util.dt import as_local, utcnow
from .const import (
ATTR_ELEMENT,
ATTR_MODALITY,
DEFAULT_NAME,
DOMAIN,
ELEMENT_AIR,
ELEMENT_EARTH,
@ -159,23 +162,21 @@ ZODIAC_ICONS = {
}
async def async_setup_platform(
async def async_setup_entry(
hass: HomeAssistant,
config: ConfigType,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the Zodiac sensor platform."""
if discovery_info is None:
return
"""Initialize the entries."""
async_add_entities([ZodiacSensor()], True)
async_add_entities([ZodiacSensor(entry_id=entry.entry_id)], True)
class ZodiacSensor(SensorEntity):
"""Representation of a Zodiac sensor."""
_attr_name = "Zodiac"
_attr_name = None
_attr_has_entity_name = True
_attr_device_class = SensorDeviceClass.ENUM
_attr_options = [
SIGN_AQUARIUS,
@ -194,6 +195,14 @@ class ZodiacSensor(SensorEntity):
_attr_translation_key = "sign"
_attr_unique_id = DOMAIN
def __init__(self, entry_id: str) -> None:
"""Initialize Zodiac sensor."""
self._attr_device_info = DeviceInfo(
name=DEFAULT_NAME,
identifiers={(DOMAIN, entry_id)},
entry_type=DeviceEntryType.SERVICE,
)
async def async_update(self) -> None:
"""Get the time and updates the state."""
today = as_local(utcnow()).date()

View file

@ -1,4 +1,14 @@
{
"config": {
"step": {
"user": {
"description": "[%key:common::config_flow::description::confirm_setup%]"
}
},
"abort": {
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]"
}
},
"entity": {
"sensor": {
"sign": {
@ -18,5 +28,11 @@
}
}
}
},
"issues": {
"deprecated_yaml": {
"title": "The Zodiac YAML configuration is being removed",
"description": "Configuring Zodiac using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the Zodiac YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue."
}
}
}

View file

@ -534,6 +534,7 @@ FLOWS = {
"zerproc",
"zeversolar",
"zha",
"zodiac",
"zwave_js",
"zwave_me",
],

View file

@ -6541,8 +6541,8 @@
"zodiac": {
"name": "Zodiac",
"integration_type": "hub",
"config_flow": false,
"iot_class": "local_polling"
"config_flow": true,
"iot_class": "calculated"
},
"zoneminder": {
"name": "ZoneMinder",

View file

@ -0,0 +1,70 @@
"""Tests for the Zodiac config flow."""
from unittest.mock import patch
import pytest
from homeassistant.components.zodiac.const import DOMAIN
from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from tests.common import MockConfigEntry
async def test_full_user_flow(hass: HomeAssistant) -> None:
"""Test the full user configuration flow."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result.get("type") == FlowResultType.FORM
assert result.get("step_id") == "user"
with patch(
"homeassistant.components.zodiac.async_setup_entry",
return_value=True,
) as mock_setup_entry:
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={},
)
assert result.get("type") == FlowResultType.CREATE_ENTRY
assert result.get("title") == "Zodiac"
assert result.get("data") == {}
assert result.get("options") == {}
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.parametrize("source", [SOURCE_USER, SOURCE_IMPORT])
async def test_single_instance_allowed(
hass: HomeAssistant,
source: str,
) -> None:
"""Test we abort if already setup."""
mock_config_entry = MockConfigEntry(domain=DOMAIN)
mock_config_entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": source}
)
assert result.get("type") == FlowResultType.ABORT
assert result.get("reason") == "single_instance_allowed"
async def test_import_flow(
hass: HomeAssistant,
) -> None:
"""Test the import configuration flow."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_IMPORT},
data={},
)
assert result.get("type") == FlowResultType.CREATE_ENTRY
assert result.get("title") == "Zodiac"
assert result.get("data") == {}
assert result.get("options") == {}

View file

@ -24,6 +24,8 @@ from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component
import homeassistant.util.dt as dt_util
from tests.common import MockConfigEntry
DAY1 = datetime(2020, 11, 15, tzinfo=dt_util.UTC)
DAY2 = datetime(2020, 4, 20, tzinfo=dt_util.UTC)
DAY3 = datetime(2020, 4, 21, tzinfo=dt_util.UTC)
@ -37,13 +39,17 @@ DAY3 = datetime(2020, 4, 21, tzinfo=dt_util.UTC)
(DAY3, SIGN_TAURUS, ELEMENT_EARTH, MODALITY_FIXED),
],
)
async def test_zodiac_day(hass: HomeAssistant, now, sign, element, modality) -> None:
async def test_zodiac_day(
hass: HomeAssistant, now: datetime, sign: str, element: str, modality: str
) -> None:
"""Test the zodiac sensor."""
hass.config.set_time_zone("UTC")
config = {DOMAIN: {}}
MockConfigEntry(
domain=DOMAIN,
).add_to_hass(hass)
with patch("homeassistant.components.zodiac.sensor.utcnow", return_value=now):
assert await async_setup_component(hass, DOMAIN, config)
assert await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done()
state = hass.states.get("sensor.zodiac")