Add config flow for zodiac (#95447)
* Add config flow for zodiac * Add config flow for zodiac * Fix feedback
This commit is contained in:
parent
abf6e0e44d
commit
4ac92d755e
10 changed files with 177 additions and 17 deletions
|
@ -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])
|
||||
|
|
31
homeassistant/components/zodiac/config_flow.py
Normal file
31
homeassistant/components/zodiac/config_flow.py
Normal 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)
|
|
@ -1,5 +1,6 @@
|
|||
"""Constants for Zodiac."""
|
||||
DOMAIN = "zodiac"
|
||||
DEFAULT_NAME = "Zodiac"
|
||||
|
||||
# Signs
|
||||
SIGN_ARIES = "aries"
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -534,6 +534,7 @@ FLOWS = {
|
|||
"zerproc",
|
||||
"zeversolar",
|
||||
"zha",
|
||||
"zodiac",
|
||||
"zwave_js",
|
||||
"zwave_me",
|
||||
],
|
||||
|
|
|
@ -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",
|
||||
|
|
70
tests/components/zodiac/test_config_flow.py
Normal file
70
tests/components/zodiac/test_config_flow.py
Normal 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") == {}
|
|
@ -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")
|
||||
|
|
Loading…
Add table
Reference in a new issue