diff --git a/homeassistant/components/cast/media_player.py b/homeassistant/components/cast/media_player.py index b6ca8dd0728..016d5162d23 100644 --- a/homeassistant/components/cast/media_player.py +++ b/homeassistant/components/cast/media_player.py @@ -473,7 +473,7 @@ class CastDevice(MediaPlayerEntity): self.hass, refresh_token.id, media_id, - timedelta(minutes=5), + timedelta(seconds=media_source.DEFAULT_EXPIRY_TIME), ) # prepend external URL diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index 98ae51341af..b69ee769d66 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -3,7 +3,7 @@ "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", "requirements": [ - "home-assistant-frontend==20210407.2" + "home-assistant-frontend==20210407.3" ], "dependencies": [ "api", diff --git a/homeassistant/components/hassio/handler.py b/homeassistant/components/hassio/handler.py index 90077261185..301d353faf0 100644 --- a/homeassistant/components/hassio/handler.py +++ b/homeassistant/components/hassio/handler.py @@ -148,7 +148,7 @@ class HassIO: This method return a coroutine. """ - return self.send_command("/discovery", method="get") + return self.send_command("/discovery", method="get", timeout=60) @api_data def get_discovery_message(self, uuid): diff --git a/homeassistant/components/kodi/manifest.json b/homeassistant/components/kodi/manifest.json index 63282ed1a9a..9ab51050704 100644 --- a/homeassistant/components/kodi/manifest.json +++ b/homeassistant/components/kodi/manifest.json @@ -3,7 +3,7 @@ "name": "Kodi", "documentation": "https://www.home-assistant.io/integrations/kodi", "requirements": [ - "pykodi==0.2.3" + "pykodi==0.2.5" ], "codeowners": [ "@OnFreund", diff --git a/homeassistant/components/media_source/__init__.py b/homeassistant/components/media_source/__init__.py index 6aa01403a5f..0ef5d460580 100644 --- a/homeassistant/components/media_source/__init__.py +++ b/homeassistant/components/media_source/__init__.py @@ -19,6 +19,8 @@ from . import local_source, models from .const import DOMAIN, URI_SCHEME, URI_SCHEME_REGEX from .error import Unresolvable +DEFAULT_EXPIRY_TIME = 3600 * 24 + def is_media_source_id(media_content_id: str): """Test if identifier is a media source.""" @@ -105,7 +107,7 @@ async def websocket_browse_media(hass, connection, msg): { vol.Required("type"): "media_source/resolve_media", vol.Required(ATTR_MEDIA_CONTENT_ID): str, - vol.Optional("expires", default=30): int, + vol.Optional("expires", default=DEFAULT_EXPIRY_TIME): int, } ) @websocket_api.async_response diff --git a/homeassistant/components/notify_events/notify.py b/homeassistant/components/notify_events/notify.py index ce7c353badb..51705453edf 100644 --- a/homeassistant/components/notify_events/notify.py +++ b/homeassistant/components/notify_events/notify.py @@ -116,12 +116,9 @@ class NotifyEventsNotificationService(BaseNotificationService): def send_message(self, message, **kwargs): """Send a message.""" - token = self.token data = kwargs.get(ATTR_DATA) or {} + token = data.get(ATTR_TOKEN, self.token) msg = self.prepare_message(message, data) - if data.get(ATTR_TOKEN, "").trim(): - token = data[ATTR_TOKEN] - msg.send(token) diff --git a/homeassistant/components/openweathermap/weather_update_coordinator.py b/homeassistant/components/openweathermap/weather_update_coordinator.py index 51e475eb754..20cc71da725 100644 --- a/homeassistant/components/openweathermap/weather_update_coordinator.py +++ b/homeassistant/components/openweathermap/weather_update_coordinator.py @@ -122,7 +122,7 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator): ATTR_API_FEELS_LIKE_TEMPERATURE: current_weather.temperature("celsius").get( "feels_like" ), - ATTR_API_DEW_POINT: (round(current_weather.dewpoint / 100, 1)), + ATTR_API_DEW_POINT: self._fmt_dewpoint(current_weather.dewpoint), ATTR_API_PRESSURE: current_weather.pressure.get("press"), ATTR_API_HUMIDITY: current_weather.humidity, ATTR_API_WIND_BEARING: current_weather.wind().get("deg"), @@ -178,6 +178,12 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator): return forecast + @staticmethod + def _fmt_dewpoint(dewpoint): + if dewpoint is not None: + return round(dewpoint / 100, 1) + return None + @staticmethod def _get_rain(rain): """Get rain data from weather data.""" diff --git a/homeassistant/components/plex/models.py b/homeassistant/components/plex/models.py index 731d5bbc7db..af1343095f0 100644 --- a/homeassistant/components/plex/models.py +++ b/homeassistant/components/plex/models.py @@ -7,7 +7,7 @@ from homeassistant.components.media_player.const import ( ) from homeassistant.util import dt as dt_util -LIVE_TV_SECTION = "-4" +LIVE_TV_SECTION = -4 class PlexSession: diff --git a/homeassistant/components/rituals_perfume_genie/switch.py b/homeassistant/components/rituals_perfume_genie/switch.py index 471be52b054..bc8e2b5e175 100644 --- a/homeassistant/components/rituals_perfume_genie/switch.py +++ b/homeassistant/components/rituals_perfume_genie/switch.py @@ -1,10 +1,15 @@ """Support for Rituals Perfume Genie switches.""" from datetime import timedelta +import logging + +import aiohttp from homeassistant.components.switch import SwitchEntity from .const import DOMAIN +_LOGGER = logging.getLogger(__name__) + SCAN_INTERVAL = timedelta(seconds=30) ON_STATE = "1" @@ -33,6 +38,7 @@ class DiffuserSwitch(SwitchEntity): def __init__(self, diffuser): """Initialize the switch.""" self._diffuser = diffuser + self._available = True @property def device_info(self): @@ -53,7 +59,7 @@ class DiffuserSwitch(SwitchEntity): @property def available(self): """Return if the device is available.""" - return self._diffuser.data["hub"]["status"] == AVAILABLE_STATE + return self._available @property def name(self): @@ -89,4 +95,10 @@ class DiffuserSwitch(SwitchEntity): async def async_update(self): """Update the data of the device.""" - await self._diffuser.update_data() + try: + await self._diffuser.update_data() + except aiohttp.ClientError: + self._available = False + _LOGGER.error("Unable to retrieve data from rituals.sense-company.com") + else: + self._available = self._diffuser.data["hub"]["status"] == AVAILABLE_STATE diff --git a/homeassistant/components/version/sensor.py b/homeassistant/components/version/sensor.py index 9d558f4ba7c..d438f391334 100644 --- a/homeassistant/components/version/sensor.py +++ b/homeassistant/components/version/sensor.py @@ -1,7 +1,9 @@ """Sensor that can display the current Home Assistant versions.""" from datetime import timedelta +import logging from pyhaversion import HaVersion, HaVersionChannel, HaVersionSource +from pyhaversion.exceptions import HaVersionFetchException, HaVersionParseException import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity @@ -59,6 +61,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( } ) +_LOGGER: logging.Logger = logging.getLogger(__name__) + async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Version sensor platform.""" @@ -114,7 +118,14 @@ class VersionData: @Throttle(TIME_BETWEEN_UPDATES) async def async_update(self): """Get the latest version information.""" - await self.api.get_version() + try: + await self.api.get_version() + except HaVersionFetchException as exception: + _LOGGER.warning(exception) + except HaVersionParseException as exception: + _LOGGER.warning( + "Could not parse data received for %s - %s", self.api.source, exception + ) class VersionSensor(SensorEntity): diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index 5825bdcda0f..5cd57e26274 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -7,7 +7,7 @@ "bellows==0.23.1", "pyserial==3.5", "pyserial-asyncio==0.5", - "zha-quirks==0.0.55", + "zha-quirks==0.0.56", "zigpy-cc==0.5.2", "zigpy-deconz==0.12.0", "zigpy==0.33.0", diff --git a/homeassistant/const.py b/homeassistant/const.py index 6848123e820..ffc2d1a3427 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 2021 MINOR_VERSION = 4 -PATCH_VERSION = "1" +PATCH_VERSION = "2" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 8, 0) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index a42e0016924..c5f0ccde05d 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -16,7 +16,7 @@ defusedxml==0.6.0 distro==1.5.0 emoji==1.2.0 hass-nabucasa==0.42.0 -home-assistant-frontend==20210407.2 +home-assistant-frontend==20210407.3 httpx==0.17.1 jinja2>=2.11.3 netdisco==2.8.2 diff --git a/requirements_all.txt b/requirements_all.txt index 954cbb80bd4..694f7c73b89 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -763,7 +763,7 @@ hole==0.5.1 holidays==0.10.5.2 # homeassistant.components.frontend -home-assistant-frontend==20210407.2 +home-assistant-frontend==20210407.3 # homeassistant.components.zwave homeassistant-pyozw==0.1.10 @@ -1476,7 +1476,7 @@ pykira==0.1.1 pykmtronic==0.0.3 # homeassistant.components.kodi -pykodi==0.2.3 +pykodi==0.2.5 # homeassistant.components.kulersky pykulersky==0.5.2 @@ -2372,7 +2372,7 @@ zengge==0.2 zeroconf==0.29.0 # homeassistant.components.zha -zha-quirks==0.0.55 +zha-quirks==0.0.56 # homeassistant.components.zhong_hong zhong_hong_hvac==1.0.9 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 28d5932284b..3936ed66128 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -412,7 +412,7 @@ hole==0.5.1 holidays==0.10.5.2 # homeassistant.components.frontend -home-assistant-frontend==20210407.2 +home-assistant-frontend==20210407.3 # homeassistant.components.zwave homeassistant-pyozw==0.1.10 @@ -518,6 +518,9 @@ netdisco==2.8.2 # homeassistant.components.nexia nexia==0.9.5 +# homeassistant.components.notify_events +notify-events==1.0.4 + # homeassistant.components.nsw_fuel_station nsw-fuel-api-client==1.0.10 @@ -784,7 +787,7 @@ pykira==0.1.1 pykmtronic==0.0.3 # homeassistant.components.kodi -pykodi==0.2.3 +pykodi==0.2.5 # homeassistant.components.kulersky pykulersky==0.5.2 @@ -1230,7 +1233,7 @@ zeep[async]==4.0.0 zeroconf==0.29.0 # homeassistant.components.zha -zha-quirks==0.0.55 +zha-quirks==0.0.56 # homeassistant.components.zha zigpy-cc==0.5.2 diff --git a/tests/components/notify_events/__init__.py b/tests/components/notify_events/__init__.py new file mode 100644 index 00000000000..5e2f9c2eaf1 --- /dev/null +++ b/tests/components/notify_events/__init__.py @@ -0,0 +1 @@ +"""Tests for the notify_events integration.""" diff --git a/tests/components/notify_events/test_init.py b/tests/components/notify_events/test_init.py new file mode 100644 index 00000000000..861be83a9cc --- /dev/null +++ b/tests/components/notify_events/test_init.py @@ -0,0 +1,12 @@ +"""The tests for notify_events.""" +from homeassistant.components.notify_events.const import DOMAIN +from homeassistant.setup import async_setup_component + + +async def test_setup(hass): + """Test setup of the integration.""" + config = {"notify_events": {"token": "ABC"}} + assert await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() + + assert DOMAIN in hass.data diff --git a/tests/components/notify_events/test_notify.py b/tests/components/notify_events/test_notify.py new file mode 100644 index 00000000000..55cf6275044 --- /dev/null +++ b/tests/components/notify_events/test_notify.py @@ -0,0 +1,38 @@ +"""The tests for notify_events.""" +from homeassistant.components.notify import ATTR_DATA, ATTR_MESSAGE, DOMAIN +from homeassistant.components.notify_events.notify import ( + ATTR_LEVEL, + ATTR_PRIORITY, + ATTR_TOKEN, +) + +from tests.common import async_mock_service + + +async def test_send_msg(hass): + """Test notify.events service.""" + notify_calls = async_mock_service(hass, DOMAIN, "events") + + await hass.services.async_call( + DOMAIN, + "events", + { + ATTR_MESSAGE: "message content", + ATTR_DATA: { + ATTR_TOKEN: "XYZ", + ATTR_LEVEL: "warning", + ATTR_PRIORITY: "high", + }, + }, + blocking=True, + ) + + assert len(notify_calls) == 1 + call = notify_calls[-1] + + assert call.domain == DOMAIN + assert call.service == "events" + assert call.data.get(ATTR_MESSAGE) == "message content" + assert call.data.get(ATTR_DATA).get(ATTR_TOKEN) == "XYZ" + assert call.data.get(ATTR_DATA).get(ATTR_LEVEL) == "warning" + assert call.data.get(ATTR_DATA).get(ATTR_PRIORITY) == "high"