Merge pull request #32414 from home-assistant/rc

0.106.3
pull/28581/head^2
Paulus Schoutsen 2020-03-02 13:43:02 -08:00 committed by GitHub
commit f7f9126610
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 272 additions and 1 deletions

View File

@ -68,6 +68,7 @@ homeassistant/components/config/* @home-assistant/core
homeassistant/components/configurator/* @home-assistant/core
homeassistant/components/conversation/* @home-assistant/core
homeassistant/components/coolmaster/* @OnFreund
homeassistant/components/coronavirus/* @home_assistant/core
homeassistant/components/counter/* @fabaff
homeassistant/components/cover/* @home-assistant/core
homeassistant/components/cpuspeed/* @fabaff

View File

@ -0,0 +1,13 @@
{
"config": {
"step": {
"user": {
"data": {
"country": "Country"
},
"title": "Pick a country to monitor"
}
},
"title": "Coronavirus"
}
}

View File

@ -0,0 +1,75 @@
"""The Coronavirus integration."""
import asyncio
from datetime import timedelta
import logging
import aiohttp
import async_timeout
import coronavirus
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers import aiohttp_client, update_coordinator
from .const import DOMAIN
PLATFORMS = ["sensor"]
async def async_setup(hass: HomeAssistant, config: dict):
"""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):
"""Set up Coronavirus from a config entry."""
for component in PLATFORMS:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, component)
)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Unload a config entry."""
unload_ok = all(
await asyncio.gather(
*[
hass.config_entries.async_forward_entry_unload(entry, component)
for component in PLATFORMS
]
)
)
return unload_ok
async def get_coordinator(hass):
"""Get the data update coordinator."""
if DOMAIN in hass.data:
return hass.data[DOMAIN]
async def async_get_cases():
try:
with async_timeout.timeout(10):
return {
case.id: case
for case in await coronavirus.get_cases(
aiohttp_client.async_get_clientsession(hass)
)
}
except (asyncio.TimeoutError, aiohttp.ClientError):
raise update_coordinator.UpdateFailed
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]

View File

@ -0,0 +1,41 @@
"""Config flow for Coronavirus integration."""
import logging
import voluptuous as vol
from homeassistant import config_entries
from . import get_coordinator
from .const import DOMAIN, OPTION_WORLDWIDE # pylint:disable=unused-import
_LOGGER = logging.getLogger(__name__)
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Coronavirus."""
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
_options = None
async def async_step_user(self, user_input=None):
"""Handle the initial step."""
errors = {}
if self._options is None:
self._options = {OPTION_WORLDWIDE: "Worldwide"}
coordinator = await get_coordinator(self.hass)
for case_id in sorted(coordinator.data):
self._options[case_id] = coordinator.data[case_id].country
if user_input is not None:
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,
)

View File

@ -0,0 +1,6 @@
"""Constants for the Coronavirus integration."""
from coronavirus import DEFAULT_SOURCE
DOMAIN = "coronavirus"
OPTION_WORLDWIDE = "__worldwide"
ATTRIBUTION = f"Data provided by {DEFAULT_SOURCE.NAME}"

View File

@ -0,0 +1,12 @@
{
"domain": "coronavirus",
"name": "Coronavirus (COVID-19)",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/coronavirus",
"requirements": ["coronavirus==1.0.1"],
"ssdp": [],
"zeroconf": [],
"homekit": {},
"dependencies": [],
"codeowners": ["@home_assistant/core"]
}

View File

@ -0,0 +1,69 @@
"""Sensor platform for the Corona virus."""
from homeassistant.const import ATTR_ATTRIBUTION
from homeassistant.helpers.entity import Entity
from . import get_coordinator
from .const import ATTRIBUTION, OPTION_WORLDWIDE
async def async_setup_entry(hass, config_entry, async_add_entities):
"""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 ("confirmed", "recovered", "deaths", "current")
)
class CoronavirusSensor(Entity):
"""Sensor representing corona virus data."""
name = None
unique_id = None
def __init__(self, coordinator, country, info_type):
"""Initialize coronavirus sensor."""
if country == OPTION_WORLDWIDE:
self.name = f"Worldwide {info_type}"
else:
self.name = f"{coordinator.data[country].country} {info_type}"
self.unique_id = f"{country}-{info_type}"
self.coordinator = coordinator
self.country = country
self.info_type = info_type
@property
def available(self):
"""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 state(self):
"""State of the sensor."""
if self.country == OPTION_WORLDWIDE:
return sum(
getattr(case, self.info_type) for case in self.coordinator.data.values()
)
return getattr(self.coordinator.data[self.country], self.info_type)
@property
def unit_of_measurement(self):
"""Return unit of measurement."""
return "people"
@property
def device_state_attributes(self):
"""Return device attributes."""
return {ATTR_ATTRIBUTION: ATTRIBUTION}
async def async_added_to_hass(self):
"""When entity is added to hass."""
self.coordinator.async_add_listener(self.async_write_ha_state)
async def async_will_remove_from_hass(self):
"""When entity will be removed from hass."""
self.coordinator.async_remove_listener(self.async_write_ha_state)

View File

@ -0,0 +1,13 @@
{
"config": {
"title": "Coronavirus",
"step": {
"user": {
"title": "Pick a country to monitor",
"data": {
"country": "Country"
}
}
}
}
}

View File

@ -1,7 +1,7 @@
"""Constants used by Home Assistant components."""
MAJOR_VERSION = 0
MINOR_VERSION = 106
PATCH_VERSION = "2"
PATCH_VERSION = "3"
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__ = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER = (3, 7, 0)

View File

@ -17,6 +17,7 @@ FLOWS = [
"cast",
"cert_expiry",
"coolmaster",
"coronavirus",
"daikin",
"deconz",
"dialogflow",

View File

@ -398,6 +398,9 @@ connect-box==0.2.5
# homeassistant.components.xiaomi_miio
construct==2.9.45
# homeassistant.components.coronavirus
coronavirus==1.0.1
# homeassistant.scripts.credstash
# credstash==1.15.0

View File

@ -143,6 +143,9 @@ colorlog==4.1.0
# homeassistant.components.xiaomi_miio
construct==2.9.45
# homeassistant.components.coronavirus
coronavirus==1.0.1
# homeassistant.scripts.credstash
# credstash==1.15.0

View File

@ -0,0 +1 @@
"""Tests for the Coronavirus integration."""

View File

@ -0,0 +1,33 @@
"""Test the Coronavirus config flow."""
from asynctest import patch
from homeassistant import config_entries, setup
from homeassistant.components.coronavirus.const import DOMAIN, OPTION_WORLDWIDE
async def test_form(hass):
"""Test we get the form."""
await setup.async_setup_component(hass, "persistent_notification", {})
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == "form"
assert result["errors"] == {}
with patch("coronavirus.get_cases", return_value=[],), patch(
"homeassistant.components.coronavirus.async_setup", return_value=True
) as mock_setup, patch(
"homeassistant.components.coronavirus.async_setup_entry", return_value=True,
) as mock_setup_entry:
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["data"] == {
"country": OPTION_WORLDWIDE,
}
await hass.async_block_till_done()
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1