diff --git a/CODEOWNERS b/CODEOWNERS index c00a0f853b4..906787e0452 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -217,8 +217,6 @@ build.json @home-assistant/supervisor /tests/components/conversation/ @home-assistant/core @synesthesiam /homeassistant/components/coolmaster/ @OnFreund /tests/components/coolmaster/ @OnFreund -/homeassistant/components/coronavirus/ @home-assistant/core -/tests/components/coronavirus/ @home-assistant/core /homeassistant/components/counter/ @fabaff /tests/components/counter/ @fabaff /homeassistant/components/cover/ @home-assistant/core diff --git a/homeassistant/components/coronavirus/__init__.py b/homeassistant/components/coronavirus/__init__.py deleted file mode 100644 index a3bc07ee0a1..00000000000 --- a/homeassistant/components/coronavirus/__init__.py +++ /dev/null @@ -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] diff --git a/homeassistant/components/coronavirus/config_flow.py b/homeassistant/components/coronavirus/config_flow.py deleted file mode 100644 index 81e4f06f57f..00000000000 --- a/homeassistant/components/coronavirus/config_flow.py +++ /dev/null @@ -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, - ) diff --git a/homeassistant/components/coronavirus/const.py b/homeassistant/components/coronavirus/const.py deleted file mode 100644 index e1ffa64e88c..00000000000 --- a/homeassistant/components/coronavirus/const.py +++ /dev/null @@ -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}" diff --git a/homeassistant/components/coronavirus/manifest.json b/homeassistant/components/coronavirus/manifest.json deleted file mode 100644 index a053b4056c0..00000000000 --- a/homeassistant/components/coronavirus/manifest.json +++ /dev/null @@ -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"] -} diff --git a/homeassistant/components/coronavirus/sensor.py b/homeassistant/components/coronavirus/sensor.py deleted file mode 100644 index 7fa7c5aed08..00000000000 --- a/homeassistant/components/coronavirus/sensor.py +++ /dev/null @@ -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) diff --git a/homeassistant/components/coronavirus/strings.json b/homeassistant/components/coronavirus/strings.json deleted file mode 100644 index e0b29d6c8db..00000000000 --- a/homeassistant/components/coronavirus/strings.json +++ /dev/null @@ -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%]" - } - } -} diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 0370c4249ee..08f43fa5b04 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -80,7 +80,6 @@ FLOWS = { "coinbase", "control4", "coolmaster", - "coronavirus", "cpuspeed", "crownstone", "daikin", diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 9517cba3486..3f5ee393800 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -881,12 +881,6 @@ "config_flow": true, "iot_class": "local_polling" }, - "coronavirus": { - "name": "Coronavirus (COVID-19)", - "integration_type": "hub", - "config_flow": true, - "iot_class": "cloud_polling" - }, "cozytouch": { "name": "Atlantic Cozytouch", "integration_type": "virtual", diff --git a/requirements_all.txt b/requirements_all.txt index bff7a8e9ebb..a6e8a1745a6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -544,9 +544,6 @@ connect-box==0.2.8 # homeassistant.components.xiaomi_miio construct==2.10.56 -# homeassistant.components.coronavirus -coronavirus==1.1.1 - # homeassistant.components.utility_meter croniter==1.0.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index de11f1502a8..2c3d757a458 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -430,9 +430,6 @@ colorthief==0.2.1 # homeassistant.components.xiaomi_miio construct==2.10.56 -# homeassistant.components.coronavirus -coronavirus==1.1.1 - # homeassistant.components.utility_meter croniter==1.0.6 diff --git a/tests/components/coronavirus/__init__.py b/tests/components/coronavirus/__init__.py deleted file mode 100644 index 2274a51506d..00000000000 --- a/tests/components/coronavirus/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Tests for the Coronavirus integration.""" diff --git a/tests/components/coronavirus/conftest.py b/tests/components/coronavirus/conftest.py deleted file mode 100644 index 227d9fa2123..00000000000 --- a/tests/components/coronavirus/conftest.py +++ /dev/null @@ -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 diff --git a/tests/components/coronavirus/test_config_flow.py b/tests/components/coronavirus/test_config_flow.py deleted file mode 100644 index 2fe7ed370e8..00000000000 --- a/tests/components/coronavirus/test_config_flow.py +++ /dev/null @@ -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" diff --git a/tests/components/coronavirus/test_init.py b/tests/components/coronavirus/test_init.py deleted file mode 100644 index eeb91e77239..00000000000 --- a/tests/components/coronavirus/test_init.py +++ /dev/null @@ -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