mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Remove the coronavirus integration (#90934)
* Remove the coronavirus integration * Run hassfest and gen_requirements_all
This commit is contained in:
parent
4667b27405
commit
8025fbf398
@ -217,8 +217,6 @@ build.json @home-assistant/supervisor
|
|||||||
/tests/components/conversation/ @home-assistant/core @synesthesiam
|
/tests/components/conversation/ @home-assistant/core @synesthesiam
|
||||||
/homeassistant/components/coolmaster/ @OnFreund
|
/homeassistant/components/coolmaster/ @OnFreund
|
||||||
/tests/components/coolmaster/ @OnFreund
|
/tests/components/coolmaster/ @OnFreund
|
||||||
/homeassistant/components/coronavirus/ @home-assistant/core
|
|
||||||
/tests/components/coronavirus/ @home-assistant/core
|
|
||||||
/homeassistant/components/counter/ @fabaff
|
/homeassistant/components/counter/ @fabaff
|
||||||
/tests/components/counter/ @fabaff
|
/tests/components/counter/ @fabaff
|
||||||
/homeassistant/components/cover/ @home-assistant/core
|
/homeassistant/components/cover/ @home-assistant/core
|
||||||
|
@ -1,88 +0,0 @@
|
|||||||
"""The Coronavirus integration."""
|
|
||||||
from datetime import timedelta
|
|
||||||
import logging
|
|
||||||
|
|
||||||
import async_timeout
|
|
||||||
import coronavirus
|
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import Platform
|
|
||||||
from homeassistant.core import HomeAssistant, callback
|
|
||||||
from homeassistant.helpers import (
|
|
||||||
aiohttp_client,
|
|
||||||
entity_registry as er,
|
|
||||||
update_coordinator,
|
|
||||||
)
|
|
||||||
from homeassistant.helpers.typing import ConfigType
|
|
||||||
|
|
||||||
from .const import DOMAIN
|
|
||||||
|
|
||||||
PLATFORMS = [Platform.SENSOR]
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|
||||||
"""Set up the Coronavirus component."""
|
|
||||||
# Make sure coordinator is initialized.
|
|
||||||
await get_coordinator(hass)
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|
||||||
"""Set up Coronavirus from a config entry."""
|
|
||||||
if isinstance(entry.data["country"], int):
|
|
||||||
hass.config_entries.async_update_entry(
|
|
||||||
entry, data={**entry.data, "country": entry.title}
|
|
||||||
)
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def _async_migrator(entity_entry: er.RegistryEntry):
|
|
||||||
"""Migrate away from unstable ID."""
|
|
||||||
country, info_type = entity_entry.unique_id.rsplit("-", 1)
|
|
||||||
if not country.isnumeric():
|
|
||||||
return None
|
|
||||||
return {"new_unique_id": f"{entry.title}-{info_type}"}
|
|
||||||
|
|
||||||
await er.async_migrate_entries(hass, entry.entry_id, _async_migrator)
|
|
||||||
|
|
||||||
if not entry.unique_id:
|
|
||||||
hass.config_entries.async_update_entry(entry, unique_id=entry.data["country"])
|
|
||||||
|
|
||||||
coordinator = await get_coordinator(hass)
|
|
||||||
if not coordinator.last_update_success:
|
|
||||||
await coordinator.async_config_entry_first_refresh()
|
|
||||||
|
|
||||||
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."""
|
|
||||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
|
||||||
|
|
||||||
|
|
||||||
async def get_coordinator(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
) -> update_coordinator.DataUpdateCoordinator:
|
|
||||||
"""Get the data update coordinator."""
|
|
||||||
if DOMAIN in hass.data:
|
|
||||||
return hass.data[DOMAIN]
|
|
||||||
|
|
||||||
async def async_get_cases():
|
|
||||||
async with async_timeout.timeout(10):
|
|
||||||
return {
|
|
||||||
case.country: case
|
|
||||||
for case in await coronavirus.get_cases(
|
|
||||||
aiohttp_client.async_get_clientsession(hass)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
hass.data[DOMAIN] = update_coordinator.DataUpdateCoordinator(
|
|
||||||
hass,
|
|
||||||
logging.getLogger(__name__),
|
|
||||||
name=DOMAIN,
|
|
||||||
update_method=async_get_cases,
|
|
||||||
update_interval=timedelta(hours=1),
|
|
||||||
)
|
|
||||||
await hass.data[DOMAIN].async_refresh()
|
|
||||||
return hass.data[DOMAIN]
|
|
@ -1,50 +0,0 @@
|
|||||||
"""Config flow for Coronavirus integration."""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
import voluptuous as vol
|
|
||||||
|
|
||||||
from homeassistant import config_entries
|
|
||||||
from homeassistant.data_entry_flow import FlowResult
|
|
||||||
|
|
||||||
from . import get_coordinator
|
|
||||||
from .const import DOMAIN, OPTION_WORLDWIDE
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|
||||||
"""Handle a config flow for Coronavirus."""
|
|
||||||
|
|
||||||
VERSION = 1
|
|
||||||
|
|
||||||
_options: dict[str, Any] | None = None
|
|
||||||
|
|
||||||
async def async_step_user(
|
|
||||||
self, user_input: dict[str, Any] | None = None
|
|
||||||
) -> FlowResult:
|
|
||||||
"""Handle the initial step."""
|
|
||||||
errors: dict[str, str] = {}
|
|
||||||
|
|
||||||
if self._options is None:
|
|
||||||
coordinator = await get_coordinator(self.hass)
|
|
||||||
if not coordinator.last_update_success or coordinator.data is None:
|
|
||||||
return self.async_abort(reason="cannot_connect")
|
|
||||||
|
|
||||||
self._options = {OPTION_WORLDWIDE: "Worldwide"}
|
|
||||||
for case in sorted(
|
|
||||||
coordinator.data.values(), key=lambda case: case.country
|
|
||||||
):
|
|
||||||
self._options[case.country] = case.country
|
|
||||||
|
|
||||||
if user_input is not None:
|
|
||||||
await self.async_set_unique_id(user_input["country"])
|
|
||||||
self._abort_if_unique_id_configured()
|
|
||||||
return self.async_create_entry(
|
|
||||||
title=self._options[user_input["country"]], data=user_input
|
|
||||||
)
|
|
||||||
|
|
||||||
return self.async_show_form(
|
|
||||||
step_id="user",
|
|
||||||
data_schema=vol.Schema({vol.Required("country"): vol.In(self._options)}),
|
|
||||||
errors=errors,
|
|
||||||
)
|
|
@ -1,6 +0,0 @@
|
|||||||
"""Constants for the Coronavirus integration."""
|
|
||||||
from coronavirus import DEFAULT_SOURCE
|
|
||||||
|
|
||||||
DOMAIN = "coronavirus"
|
|
||||||
OPTION_WORLDWIDE = "__worldwide"
|
|
||||||
ATTRIBUTION = f"Data provided by {DEFAULT_SOURCE.NAME}"
|
|
@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"domain": "coronavirus",
|
|
||||||
"name": "Coronavirus (COVID-19)",
|
|
||||||
"codeowners": ["@home-assistant/core"],
|
|
||||||
"config_flow": true,
|
|
||||||
"documentation": "https://www.home-assistant.io/integrations/coronavirus",
|
|
||||||
"iot_class": "cloud_polling",
|
|
||||||
"loggers": ["coronavirus"],
|
|
||||||
"requirements": ["coronavirus==1.1.1"]
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
"""Sensor platform for the Corona virus."""
|
|
||||||
from homeassistant.components.sensor import SensorEntity
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
|
||||||
|
|
||||||
from . import get_coordinator
|
|
||||||
from .const import ATTRIBUTION, OPTION_WORLDWIDE
|
|
||||||
|
|
||||||
SENSORS = {
|
|
||||||
"confirmed": "mdi:emoticon-neutral-outline",
|
|
||||||
"current": "mdi:emoticon-sad-outline",
|
|
||||||
"recovered": "mdi:emoticon-happy-outline",
|
|
||||||
"deaths": "mdi:emoticon-cry-outline",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
config_entry: ConfigEntry,
|
|
||||||
async_add_entities: AddEntitiesCallback,
|
|
||||||
) -> None:
|
|
||||||
"""Defer sensor setup to the shared sensor module."""
|
|
||||||
coordinator = await get_coordinator(hass)
|
|
||||||
|
|
||||||
async_add_entities(
|
|
||||||
CoronavirusSensor(coordinator, config_entry.data["country"], info_type)
|
|
||||||
for info_type in SENSORS
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class CoronavirusSensor(CoordinatorEntity, SensorEntity):
|
|
||||||
"""Sensor representing corona virus data."""
|
|
||||||
|
|
||||||
_attr_attribution = ATTRIBUTION
|
|
||||||
_attr_native_unit_of_measurement = "people"
|
|
||||||
|
|
||||||
def __init__(self, coordinator, country, info_type):
|
|
||||||
"""Initialize coronavirus sensor."""
|
|
||||||
super().__init__(coordinator)
|
|
||||||
self._attr_icon = SENSORS[info_type]
|
|
||||||
self._attr_unique_id = f"{country}-{info_type}"
|
|
||||||
if country == OPTION_WORLDWIDE:
|
|
||||||
self._attr_name = f"Worldwide Coronavirus {info_type}"
|
|
||||||
else:
|
|
||||||
self._attr_name = (
|
|
||||||
f"{coordinator.data[country].country} Coronavirus {info_type}"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.country = country
|
|
||||||
self.info_type = info_type
|
|
||||||
|
|
||||||
@property
|
|
||||||
def available(self) -> bool:
|
|
||||||
"""Return if sensor is available."""
|
|
||||||
return self.coordinator.last_update_success and (
|
|
||||||
self.country in self.coordinator.data or self.country == OPTION_WORLDWIDE
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def native_value(self):
|
|
||||||
"""State of the sensor."""
|
|
||||||
if self.country == OPTION_WORLDWIDE:
|
|
||||||
sum_cases = 0
|
|
||||||
for case in self.coordinator.data.values():
|
|
||||||
if (value := getattr(case, self.info_type)) is None:
|
|
||||||
continue
|
|
||||||
sum_cases += value
|
|
||||||
|
|
||||||
return sum_cases
|
|
||||||
|
|
||||||
return getattr(self.coordinator.data[self.country], self.info_type)
|
|
@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"config": {
|
|
||||||
"step": {
|
|
||||||
"user": {
|
|
||||||
"title": "Pick a country to monitor",
|
|
||||||
"data": { "country": "Country" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"abort": {
|
|
||||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -80,7 +80,6 @@ FLOWS = {
|
|||||||
"coinbase",
|
"coinbase",
|
||||||
"control4",
|
"control4",
|
||||||
"coolmaster",
|
"coolmaster",
|
||||||
"coronavirus",
|
|
||||||
"cpuspeed",
|
"cpuspeed",
|
||||||
"crownstone",
|
"crownstone",
|
||||||
"daikin",
|
"daikin",
|
||||||
|
@ -881,12 +881,6 @@
|
|||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"iot_class": "local_polling"
|
"iot_class": "local_polling"
|
||||||
},
|
},
|
||||||
"coronavirus": {
|
|
||||||
"name": "Coronavirus (COVID-19)",
|
|
||||||
"integration_type": "hub",
|
|
||||||
"config_flow": true,
|
|
||||||
"iot_class": "cloud_polling"
|
|
||||||
},
|
|
||||||
"cozytouch": {
|
"cozytouch": {
|
||||||
"name": "Atlantic Cozytouch",
|
"name": "Atlantic Cozytouch",
|
||||||
"integration_type": "virtual",
|
"integration_type": "virtual",
|
||||||
|
@ -544,9 +544,6 @@ connect-box==0.2.8
|
|||||||
# homeassistant.components.xiaomi_miio
|
# homeassistant.components.xiaomi_miio
|
||||||
construct==2.10.56
|
construct==2.10.56
|
||||||
|
|
||||||
# homeassistant.components.coronavirus
|
|
||||||
coronavirus==1.1.1
|
|
||||||
|
|
||||||
# homeassistant.components.utility_meter
|
# homeassistant.components.utility_meter
|
||||||
croniter==1.0.6
|
croniter==1.0.6
|
||||||
|
|
||||||
|
@ -430,9 +430,6 @@ colorthief==0.2.1
|
|||||||
# homeassistant.components.xiaomi_miio
|
# homeassistant.components.xiaomi_miio
|
||||||
construct==2.10.56
|
construct==2.10.56
|
||||||
|
|
||||||
# homeassistant.components.coronavirus
|
|
||||||
coronavirus==1.1.1
|
|
||||||
|
|
||||||
# homeassistant.components.utility_meter
|
# homeassistant.components.utility_meter
|
||||||
croniter==1.0.6
|
croniter==1.0.6
|
||||||
|
|
||||||
|
@ -1 +0,0 @@
|
|||||||
"""Tests for the Coronavirus integration."""
|
|
@ -1,34 +0,0 @@
|
|||||||
"""Test helpers."""
|
|
||||||
from collections.abc import Generator
|
|
||||||
from unittest.mock import AsyncMock, Mock, patch
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_setup_entry() -> Generator[AsyncMock, None, None]:
|
|
||||||
"""Override async_setup_entry."""
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.coronavirus.async_setup_entry", return_value=True
|
|
||||||
) as mock_setup_entry:
|
|
||||||
yield mock_setup_entry
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
|
||||||
def mock_cases():
|
|
||||||
"""Mock coronavirus cases."""
|
|
||||||
with patch(
|
|
||||||
"coronavirus.get_cases",
|
|
||||||
return_value=[
|
|
||||||
Mock(country="Netherlands", confirmed=10, recovered=8, deaths=1, current=1),
|
|
||||||
Mock(country="Germany", confirmed=1, recovered=0, deaths=0, current=0),
|
|
||||||
Mock(
|
|
||||||
country="Sweden",
|
|
||||||
confirmed=None,
|
|
||||||
recovered=None,
|
|
||||||
deaths=None,
|
|
||||||
current=None,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
) as mock_get_cases:
|
|
||||||
yield mock_get_cases
|
|
@ -1,53 +0,0 @@
|
|||||||
"""Test the Coronavirus config flow."""
|
|
||||||
from unittest.mock import AsyncMock, MagicMock, patch
|
|
||||||
|
|
||||||
from aiohttp import ClientError
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from homeassistant import config_entries
|
|
||||||
from homeassistant.components.coronavirus.const import DOMAIN, OPTION_WORLDWIDE
|
|
||||||
from homeassistant.core import HomeAssistant
|
|
||||||
|
|
||||||
pytestmark = pytest.mark.usefixtures("mock_setup_entry")
|
|
||||||
|
|
||||||
|
|
||||||
async def test_form(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None:
|
|
||||||
"""Test we get the form."""
|
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
|
||||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
||||||
)
|
|
||||||
assert result["type"] == "form"
|
|
||||||
assert result["errors"] == {}
|
|
||||||
|
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
|
||||||
result["flow_id"],
|
|
||||||
{"country": OPTION_WORLDWIDE},
|
|
||||||
)
|
|
||||||
assert result2["type"] == "create_entry"
|
|
||||||
assert result2["title"] == "Worldwide"
|
|
||||||
assert result2["result"].unique_id == OPTION_WORLDWIDE
|
|
||||||
assert result2["data"] == {
|
|
||||||
"country": OPTION_WORLDWIDE,
|
|
||||||
}
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
mock_setup_entry.assert_called_once()
|
|
||||||
|
|
||||||
|
|
||||||
@patch(
|
|
||||||
"coronavirus.get_cases",
|
|
||||||
side_effect=ClientError,
|
|
||||||
)
|
|
||||||
async def test_abort_on_connection_error(
|
|
||||||
mock_get_cases: MagicMock, hass: HomeAssistant
|
|
||||||
) -> None:
|
|
||||||
"""Test we abort on connection error."""
|
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
|
||||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
||||||
)
|
|
||||||
|
|
||||||
assert "type" in result
|
|
||||||
assert result["type"] == "abort"
|
|
||||||
assert "reason" in result
|
|
||||||
assert result["reason"] == "cannot_connect"
|
|
@ -1,72 +0,0 @@
|
|||||||
"""Test init of Coronavirus integration."""
|
|
||||||
from unittest.mock import MagicMock, patch
|
|
||||||
|
|
||||||
from aiohttp import ClientError
|
|
||||||
|
|
||||||
from homeassistant.components.coronavirus.const import DOMAIN, OPTION_WORLDWIDE
|
|
||||||
from homeassistant.config_entries import ConfigEntryState
|
|
||||||
from homeassistant.core import HomeAssistant
|
|
||||||
from homeassistant.helpers import entity_registry as er
|
|
||||||
from homeassistant.setup import async_setup_component
|
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, mock_registry
|
|
||||||
|
|
||||||
|
|
||||||
async def test_migration(hass: HomeAssistant) -> None:
|
|
||||||
"""Test that we can migrate coronavirus to stable unique ID."""
|
|
||||||
nl_entry = MockConfigEntry(domain=DOMAIN, title="Netherlands", data={"country": 34})
|
|
||||||
nl_entry.add_to_hass(hass)
|
|
||||||
worldwide_entry = MockConfigEntry(
|
|
||||||
domain=DOMAIN, title="Worldwide", data={"country": OPTION_WORLDWIDE}
|
|
||||||
)
|
|
||||||
worldwide_entry.add_to_hass(hass)
|
|
||||||
mock_registry(
|
|
||||||
hass,
|
|
||||||
{
|
|
||||||
"sensor.netherlands_confirmed": er.RegistryEntry(
|
|
||||||
entity_id="sensor.netherlands_confirmed",
|
|
||||||
unique_id="34-confirmed",
|
|
||||||
platform="coronavirus",
|
|
||||||
config_entry_id=nl_entry.entry_id,
|
|
||||||
),
|
|
||||||
"sensor.worldwide_confirmed": er.RegistryEntry(
|
|
||||||
entity_id="sensor.worldwide_confirmed",
|
|
||||||
unique_id="__worldwide-confirmed",
|
|
||||||
platform="coronavirus",
|
|
||||||
config_entry_id=worldwide_entry.entry_id,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert await async_setup_component(hass, DOMAIN, {})
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
ent_reg = er.async_get(hass)
|
|
||||||
|
|
||||||
sensor_nl = ent_reg.async_get("sensor.netherlands_confirmed")
|
|
||||||
assert sensor_nl.unique_id == "Netherlands-confirmed"
|
|
||||||
|
|
||||||
sensor_worldwide = ent_reg.async_get("sensor.worldwide_confirmed")
|
|
||||||
assert sensor_worldwide.unique_id == "__worldwide-confirmed"
|
|
||||||
|
|
||||||
assert hass.states.get("sensor.netherlands_confirmed").state == "10"
|
|
||||||
assert hass.states.get("sensor.worldwide_confirmed").state == "11"
|
|
||||||
|
|
||||||
assert nl_entry.unique_id == "Netherlands"
|
|
||||||
assert worldwide_entry.unique_id == OPTION_WORLDWIDE
|
|
||||||
|
|
||||||
|
|
||||||
@patch(
|
|
||||||
"coronavirus.get_cases",
|
|
||||||
side_effect=ClientError,
|
|
||||||
)
|
|
||||||
async def test_config_entry_not_ready(
|
|
||||||
mock_get_cases: MagicMock, hass: HomeAssistant
|
|
||||||
) -> None:
|
|
||||||
"""Test the configuration entry not ready."""
|
|
||||||
entry = MockConfigEntry(domain=DOMAIN, title="Netherlands", data={"country": 34})
|
|
||||||
entry.add_to_hass(hass)
|
|
||||||
|
|
||||||
assert await async_setup_component(hass, DOMAIN, {})
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert entry.state is ConfigEntryState.SETUP_RETRY
|
|
Loading…
x
Reference in New Issue
Block a user