Merge pull request #51902 from home-assistant/rc

This commit is contained in:
Franck Nijhof 2021-06-15 20:37:40 +02:00 committed by GitHub
commit f2bc69a653
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 80 additions and 76 deletions

View File

@ -3,7 +3,7 @@
"name": "Daikin AC", "name": "Daikin AC",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/daikin", "documentation": "https://www.home-assistant.io/integrations/daikin",
"requirements": ["pydaikin==2.4.1"], "requirements": ["pydaikin==2.4.2"],
"codeowners": ["@fredrike"], "codeowners": ["@fredrike"],
"zeroconf": ["_dkapi._tcp.local."], "zeroconf": ["_dkapi._tcp.local."],
"quality_scale": "platinum", "quality_scale": "platinum",

View File

@ -2,7 +2,7 @@
from datetime import date from datetime import date
import logging import logging
from garminconnect_aio import ( from garminconnect_ha import (
Garmin, Garmin,
GarminConnectAuthenticationError, GarminConnectAuthenticationError,
GarminConnectConnectionError, GarminConnectConnectionError,
@ -13,7 +13,6 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.util import Throttle from homeassistant.util import Throttle
from .const import DEFAULT_UPDATE_INTERVAL, DOMAIN from .const import DEFAULT_UPDATE_INTERVAL, DOMAIN
@ -26,14 +25,13 @@ PLATFORMS = ["sensor"]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Set up Garmin Connect from a config entry.""" """Set up Garmin Connect from a config entry."""
websession = async_get_clientsession(hass)
username: str = entry.data[CONF_USERNAME] username: str = entry.data[CONF_USERNAME]
password: str = entry.data[CONF_PASSWORD] password: str = entry.data[CONF_PASSWORD]
garmin_client = Garmin(websession, username, password) api = Garmin(username, password)
try: try:
await garmin_client.login() await hass.async_add_executor_job(api.login)
except ( except (
GarminConnectAuthenticationError, GarminConnectAuthenticationError,
GarminConnectTooManyRequestsError, GarminConnectTooManyRequestsError,
@ -49,7 +47,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
_LOGGER.exception("Unknown error occurred during Garmin Connect login request") _LOGGER.exception("Unknown error occurred during Garmin Connect login request")
return False return False
garmin_data = GarminConnectData(hass, garmin_client) garmin_data = GarminConnectData(hass, api)
hass.data.setdefault(DOMAIN, {}) hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = garmin_data hass.data[DOMAIN][entry.entry_id] = garmin_data
@ -81,14 +79,20 @@ class GarminConnectData:
today = date.today() today = date.today()
try: try:
summary = await self.client.get_user_summary(today.isoformat()) summary = await self.hass.async_add_executor_job(
body = await self.client.get_body_composition(today.isoformat()) self.client.get_user_summary, today.isoformat()
)
body = await self.hass.async_add_executor_job(
self.client.get_body_composition, today.isoformat()
)
self.data = { self.data = {
**summary, **summary,
**body["totalAverage"], **body["totalAverage"],
} }
self.data["nextAlarm"] = await self.client.get_device_alarms() self.data["nextAlarm"] = await self.hass.async_add_executor_job(
self.client.get_device_alarms
)
except ( except (
GarminConnectAuthenticationError, GarminConnectAuthenticationError,
GarminConnectTooManyRequestsError, GarminConnectTooManyRequestsError,

View File

@ -1,7 +1,7 @@
"""Config flow for Garmin Connect integration.""" """Config flow for Garmin Connect integration."""
import logging import logging
from garminconnect_aio import ( from garminconnect_ha import (
Garmin, Garmin,
GarminConnectAuthenticationError, GarminConnectAuthenticationError,
GarminConnectConnectionError, GarminConnectConnectionError,
@ -11,7 +11,6 @@ import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.const import CONF_ID, CONF_PASSWORD, CONF_USERNAME from homeassistant.const import CONF_ID, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN from .const import DOMAIN
@ -38,15 +37,14 @@ class GarminConnectConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
if user_input is None: if user_input is None:
return await self._show_setup_form() return await self._show_setup_form()
websession = async_get_clientsession(self.hass)
username = user_input[CONF_USERNAME] username = user_input[CONF_USERNAME]
password = user_input[CONF_PASSWORD] password = user_input[CONF_PASSWORD]
garmin_client = Garmin(websession, username, password) api = Garmin(username, password)
errors = {} errors = {}
try: try:
await garmin_client.login() await self.hass.async_add_executor_job(api.login)
except GarminConnectConnectionError: except GarminConnectConnectionError:
errors["base"] = "cannot_connect" errors["base"] = "cannot_connect"
return await self._show_setup_form(errors) return await self._show_setup_form(errors)

View File

@ -2,8 +2,8 @@
"domain": "garmin_connect", "domain": "garmin_connect",
"name": "Garmin Connect", "name": "Garmin Connect",
"documentation": "https://www.home-assistant.io/integrations/garmin_connect", "documentation": "https://www.home-assistant.io/integrations/garmin_connect",
"requirements": ["garminconnect_aio==0.1.4"], "requirements": ["garminconnect_ha==0.1.6"],
"codeowners": ["@cyberjunky"], "codeowners": ["@cyberjunky"],
"config_flow": true, "config_flow": true,
"iot_class": "cloud_polling" "iot_class": "cloud_polling"
} }

View File

@ -2,7 +2,7 @@
"domain": "ialarm", "domain": "ialarm",
"name": "Antifurto365 iAlarm", "name": "Antifurto365 iAlarm",
"documentation": "https://www.home-assistant.io/integrations/ialarm", "documentation": "https://www.home-assistant.io/integrations/ialarm",
"requirements": ["pyialarm==1.8.1"], "requirements": ["pyialarm==1.9.0"],
"codeowners": ["@RyuzakiKK"], "codeowners": ["@RyuzakiKK"],
"config_flow": true, "config_flow": true,
"iot_class": "local_polling" "iot_class": "local_polling"

View File

@ -5,7 +5,7 @@ from typing import Final
MAJOR_VERSION: Final = 2021 MAJOR_VERSION: Final = 2021
MINOR_VERSION: Final = 6 MINOR_VERSION: Final = 6
PATCH_VERSION: Final = "4" PATCH_VERSION: Final = "5"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 8, 0) REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 8, 0)

View File

@ -63,3 +63,7 @@ enum34==1000000000.0.0
typing==1000000000.0.0 typing==1000000000.0.0
uuid==1000000000.0.0 uuid==1000000000.0.0
# httpcore 0.13.4 breaks several integrations
# https://github.com/home-assistant/core/issues/51778
httpcore==0.13.3

View File

@ -635,7 +635,7 @@ gTTS==2.2.2
garages-amsterdam==2.1.1 garages-amsterdam==2.1.1
# homeassistant.components.garmin_connect # homeassistant.components.garmin_connect
garminconnect_aio==0.1.4 garminconnect_ha==0.1.6
# homeassistant.components.geniushub # homeassistant.components.geniushub
geniushub-client==0.6.30 geniushub-client==0.6.30
@ -1352,7 +1352,7 @@ pycsspeechtts==1.0.4
# pycups==1.9.73 # pycups==1.9.73
# homeassistant.components.daikin # homeassistant.components.daikin
pydaikin==2.4.1 pydaikin==2.4.2
# homeassistant.components.danfoss_air # homeassistant.components.danfoss_air
pydanfossair==0.1.0 pydanfossair==0.1.0
@ -1464,7 +1464,7 @@ pyhomematic==0.1.72
pyhomeworks==0.0.6 pyhomeworks==0.0.6
# homeassistant.components.ialarm # homeassistant.components.ialarm
pyialarm==1.8.1 pyialarm==1.9.0
# homeassistant.components.icloud # homeassistant.components.icloud
pyicloud==0.10.2 pyicloud==0.10.2

View File

@ -341,7 +341,7 @@ gTTS==2.2.2
garages-amsterdam==2.1.1 garages-amsterdam==2.1.1
# homeassistant.components.garmin_connect # homeassistant.components.garmin_connect
garminconnect_aio==0.1.4 garminconnect_ha==0.1.6
# homeassistant.components.geo_json_events # homeassistant.components.geo_json_events
# homeassistant.components.usgs_earthquakes_feed # homeassistant.components.usgs_earthquakes_feed
@ -747,7 +747,7 @@ pycomfoconnect==0.4
pycoolmasternet-async==0.1.2 pycoolmasternet-async==0.1.2
# homeassistant.components.daikin # homeassistant.components.daikin
pydaikin==2.4.1 pydaikin==2.4.2
# homeassistant.components.deconz # homeassistant.components.deconz
pydeconz==79 pydeconz==79
@ -808,7 +808,7 @@ pyhiveapi==0.4.2
pyhomematic==0.1.72 pyhomematic==0.1.72
# homeassistant.components.ialarm # homeassistant.components.ialarm
pyialarm==1.8.1 pyialarm==1.9.0
# homeassistant.components.icloud # homeassistant.components.icloud
pyicloud==0.10.2 pyicloud==0.10.2

View File

@ -83,6 +83,10 @@ enum34==1000000000.0.0
typing==1000000000.0.0 typing==1000000000.0.0
uuid==1000000000.0.0 uuid==1000000000.0.0
# httpcore 0.13.4 breaks several integrations
# https://github.com/home-assistant/core/issues/51778
httpcore==0.13.3
""" """
IGNORE_PRE_COMMIT_HOOK_ID = ( IGNORE_PRE_COMMIT_HOOK_ID = (

View File

@ -1,12 +1,11 @@
"""Test the Garmin Connect config flow.""" """Test the Garmin Connect config flow."""
from unittest.mock import patch from unittest.mock import patch
from garminconnect_aio import ( from garminconnect_ha import (
GarminConnectAuthenticationError, GarminConnectAuthenticationError,
GarminConnectConnectionError, GarminConnectConnectionError,
GarminConnectTooManyRequestsError, GarminConnectTooManyRequestsError,
) )
import pytest
from homeassistant import config_entries, data_entry_flow from homeassistant import config_entries, data_entry_flow
from homeassistant.components.garmin_connect.const import DOMAIN from homeassistant.components.garmin_connect.const import DOMAIN
@ -21,37 +20,23 @@ MOCK_CONF = {
} }
@pytest.fixture(name="mock_garmin_connect")
def mock_garmin():
"""Mock Garmin Connect."""
with patch(
"homeassistant.components.garmin_connect.config_flow.Garmin",
) as garmin:
garmin.return_value.login.return_value = MOCK_CONF[CONF_ID]
yield garmin.return_value
async def test_show_form(hass): async def test_show_form(hass):
"""Test that the form is served with no input.""" """Test that the form is served with no input."""
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["errors"] == {}
assert result["step_id"] == config_entries.SOURCE_USER assert result["step_id"] == config_entries.SOURCE_USER
async def test_step_user(hass): async def test_step_user(hass):
"""Test registering an integration and finishing flow works.""" """Test registering an integration and finishing flow works."""
with patch( with patch(
"homeassistant.components.garmin_connect.Garmin.login",
return_value="my@email.address",
), patch(
"homeassistant.components.garmin_connect.async_setup_entry", return_value=True "homeassistant.components.garmin_connect.async_setup_entry", return_value=True
): ), patch(
"homeassistant.components.garmin_connect.config_flow.Garmin",
) as garmin:
garmin.return_value.login.return_value = MOCK_CONF[CONF_ID]
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}, data=MOCK_CONF DOMAIN, context={"source": config_entries.SOURCE_USER}, data=MOCK_CONF
) )
@ -59,60 +44,69 @@ async def test_step_user(hass):
assert result["data"] == MOCK_CONF assert result["data"] == MOCK_CONF
async def test_connection_error(hass, mock_garmin_connect): async def test_connection_error(hass):
"""Test for connection error.""" """Test for connection error."""
mock_garmin_connect.login.side_effect = GarminConnectConnectionError("errormsg") with patch(
result = await hass.config_entries.flow.async_init( "homeassistant.components.garmin_connect.Garmin.login",
DOMAIN, context={"source": config_entries.SOURCE_USER}, data=MOCK_CONF side_effect=GarminConnectConnectionError("errormsg"),
) ):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": "user"}, data=MOCK_CONF
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["errors"] == {"base": "cannot_connect"} assert result["errors"] == {"base": "cannot_connect"}
async def test_authentication_error(hass, mock_garmin_connect): async def test_authentication_error(hass):
"""Test for authentication error.""" """Test for authentication error."""
mock_garmin_connect.login.side_effect = GarminConnectAuthenticationError("errormsg") with patch(
result = await hass.config_entries.flow.async_init( "homeassistant.components.garmin_connect.Garmin.login",
DOMAIN, context={"source": config_entries.SOURCE_USER}, data=MOCK_CONF side_effect=GarminConnectAuthenticationError("errormsg"),
) ):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": "user"}, data=MOCK_CONF
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["errors"] == {"base": "invalid_auth"} assert result["errors"] == {"base": "invalid_auth"}
async def test_toomanyrequest_error(hass, mock_garmin_connect): async def test_toomanyrequest_error(hass):
"""Test for toomanyrequests error.""" """Test for toomanyrequests error."""
mock_garmin_connect.login.side_effect = GarminConnectTooManyRequestsError( with patch(
"errormsg" "homeassistant.components.garmin_connect.Garmin.login",
) side_effect=GarminConnectTooManyRequestsError("errormsg"),
result = await hass.config_entries.flow.async_init( ):
DOMAIN, context={"source": config_entries.SOURCE_USER}, data=MOCK_CONF result = await hass.config_entries.flow.async_init(
) DOMAIN, context={"source": "user"}, data=MOCK_CONF
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["errors"] == {"base": "too_many_requests"} assert result["errors"] == {"base": "too_many_requests"}
async def test_unknown_error(hass, mock_garmin_connect): async def test_unknown_error(hass):
"""Test for unknown error.""" """Test for unknown error."""
mock_garmin_connect.login.side_effect = Exception with patch(
result = await hass.config_entries.flow.async_init( "homeassistant.components.garmin_connect.Garmin.login",
DOMAIN, context={"source": config_entries.SOURCE_USER}, data=MOCK_CONF side_effect=Exception,
) ):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": "user"}, data=MOCK_CONF
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["errors"] == {"base": "unknown"} assert result["errors"] == {"base": "unknown"}
async def test_abort_if_already_setup(hass): async def test_abort_if_already_setup(hass):
"""Test abort if already setup.""" """Test abort if already setup."""
MockConfigEntry(
domain=DOMAIN, data=MOCK_CONF, unique_id=MOCK_CONF[CONF_ID]
).add_to_hass(hass)
with patch( with patch(
"homeassistant.components.garmin_connect.config_flow.Garmin", autospec=True "homeassistant.components.garmin_connect.config_flow.Garmin",
) as garmin: ):
garmin.return_value.login.return_value = MOCK_CONF[CONF_ID] entry = MockConfigEntry(
domain=DOMAIN, data=MOCK_CONF, unique_id=MOCK_CONF[CONF_ID]
)
entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": "user"}, data=MOCK_CONF DOMAIN, context={"source": "user"}, data=MOCK_CONF
) )
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
assert result["reason"] == "already_configured" assert result["reason"] == "already_configured"