Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2022.4.2 #69835

Merged
merged 24 commits into from Apr 11, 2022
Merged

2022.4.2 #69835

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
509d6ff
Update python-mpd2 to 3.0.5 (#69304)
azrdev Apr 10, 2022
5a408d4
Fix Netgear switch state update (#69597)
starkillerOG Apr 10, 2022
39e9270
Fix upnp subscription in SamsungTV (#69652)
epenet Apr 9, 2022
b1eda25
Fix soundtouch service calls (#69655)
KNXBroker Apr 9, 2022
269405a
Suppress Upnp parsing errors in SamsungTV (#69664)
epenet Apr 8, 2022
39e4d3e
Add None guard for zwave_js humidifier entity (#69667)
EiNSTeiN- Apr 8, 2022
87ba8a5
Fix Shelly gen2 cover unavailable when not calibrated (#69671)
thecode Apr 9, 2022
2298a1f
Refresh google calendar tokens with invalid expiration times (#69679)
allenporter Apr 9, 2022
2c48f28
Support webp still image format in generic camera (#69718)
davet2001 Apr 9, 2022
2ca8a0e
Increase tplink effects random seed allowed range to 1-600 (#69725)
bdraco Apr 9, 2022
0d7cbb8
Bump aio_georss_gdacs to 0.7 (#69743)
exxamalte Apr 10, 2022
d9253fd
Fix SleepIQ firmness number step and min values (#69757)
mfugate1 Apr 9, 2022
0ebd9e0
Fix unifiprotect for 2.0.0-beta2 of UniFi Protect (#69762)
AngellusMortis Apr 9, 2022
7c06514
Upgrade pynina to 0.1.8 (#69771)
DeerMaximum Apr 10, 2022
16a1a93
Handle expired credentials in reauth in google calendar initializatio…
allenporter Apr 10, 2022
5c4df65
Bump rtsp-to-webrtc to 0.5.1 (#69776)
allenporter Apr 10, 2022
506f8c1
Bump slixmpp to 1.8.2 (#69794)
michaeldavie Apr 10, 2022
2871ac4
Fix converting (value, unit) tuples if value is None (#69802)
rikroe Apr 10, 2022
5d4c1d9
Reduce API limit for tomorrow.io (#69818)
raman325 Apr 10, 2022
8e3e6ef
Speed up Plex playback for multiple videos (#69821)
jjlawren Apr 11, 2022
02eec73
Retry on more Plex connection failures during startup (#69822)
jjlawren Apr 11, 2022
3e92659
Downgrade av to 8.1.0 to fix memory leak (#69833)
bdraco Apr 11, 2022
2fad42c
Bumped version to 2022.4.2
balloob Apr 11, 2022
f6aead6
Don't test config on yaml import for generic camera (#69714)
davet2001 Apr 9, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 16 additions & 15 deletions homeassistant/components/bmw_connected_drive/sensor.py
Expand Up @@ -46,6 +46,17 @@ class BMWSensorEntityDescription(SensorEntityDescription):
value: Callable = lambda x, y: x


def convert_and_round(
state: tuple,
converter: Callable[[float | None, str], float],
precision: int,
) -> float | None:
"""Safely convert and round a value from a Tuple[value, unit]."""
if state[0] is None:
return None
return round(converter(state[0], UNIT_MAP.get(state[1], state[1])), precision)


SENSOR_TYPES: dict[str, BMWSensorEntityDescription] = {
# --- Generic ---
"charging_start_time": BMWSensorEntityDescription(
Expand Down Expand Up @@ -78,45 +89,35 @@ class BMWSensorEntityDescription(SensorEntityDescription):
icon="mdi:speedometer",
unit_metric=LENGTH_KILOMETERS,
unit_imperial=LENGTH_MILES,
value=lambda x, hass: round(
hass.config.units.length(x[0], UNIT_MAP.get(x[1], x[1])), 2
),
value=lambda x, hass: convert_and_round(x, hass.config.units.length, 2),
),
"remaining_range_total": BMWSensorEntityDescription(
key="remaining_range_total",
icon="mdi:map-marker-distance",
unit_metric=LENGTH_KILOMETERS,
unit_imperial=LENGTH_MILES,
value=lambda x, hass: round(
hass.config.units.length(x[0], UNIT_MAP.get(x[1], x[1])), 2
),
value=lambda x, hass: convert_and_round(x, hass.config.units.length, 2),
),
"remaining_range_electric": BMWSensorEntityDescription(
key="remaining_range_electric",
icon="mdi:map-marker-distance",
unit_metric=LENGTH_KILOMETERS,
unit_imperial=LENGTH_MILES,
value=lambda x, hass: round(
hass.config.units.length(x[0], UNIT_MAP.get(x[1], x[1])), 2
),
value=lambda x, hass: convert_and_round(x, hass.config.units.length, 2),
),
"remaining_range_fuel": BMWSensorEntityDescription(
key="remaining_range_fuel",
icon="mdi:map-marker-distance",
unit_metric=LENGTH_KILOMETERS,
unit_imperial=LENGTH_MILES,
value=lambda x, hass: round(
hass.config.units.length(x[0], UNIT_MAP.get(x[1], x[1])), 2
),
value=lambda x, hass: convert_and_round(x, hass.config.units.length, 2),
),
"remaining_fuel": BMWSensorEntityDescription(
key="remaining_fuel",
icon="mdi:gas-station",
unit_metric=VOLUME_LITERS,
unit_imperial=VOLUME_GALLONS,
value=lambda x, hass: round(
hass.config.units.volume(x[0], UNIT_MAP.get(x[1], x[1])), 2
),
value=lambda x, hass: convert_and_round(x, hass.config.units.volume, 2),
),
"fuel_percent": BMWSensorEntityDescription(
key="fuel_percent",
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/gdacs/manifest.json
Expand Up @@ -3,7 +3,7 @@
"name": "Global Disaster Alert and Coordination System (GDACS)",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/gdacs",
"requirements": ["aio_georss_gdacs==0.5"],
"requirements": ["aio_georss_gdacs==0.7"],
"codeowners": ["@exxamalte"],
"quality_scale": "platinum",
"iot_class": "cloud_polling"
Expand Down
26 changes: 6 additions & 20 deletions homeassistant/components/generic/config_flow.py
Expand Up @@ -58,7 +58,7 @@
CONF_VERIFY_SSL: True,
}

SUPPORTED_IMAGE_TYPES = {"png", "jpeg", "gif", "svg+xml"}
SUPPORTED_IMAGE_TYPES = {"png", "jpeg", "gif", "svg+xml", "webp"}


def build_schema(
Expand Down Expand Up @@ -324,32 +324,18 @@ async def async_step_import(self, import_config) -> FlowResult:
# abort if we've already got this one.
if self.check_for_existing(import_config):
return self.async_abort(reason="already_exists")
errors, still_format = await async_test_still(self.hass, import_config)
if errors.get(CONF_STILL_IMAGE_URL) == "template_error":
_LOGGER.warning(
"Could not render template, but it could be that "
"referenced entities are still initialising. "
"Continuing assuming that imported YAML template is valid"
)
errors.pop(CONF_STILL_IMAGE_URL)
still_format = import_config.get(CONF_CONTENT_TYPE, "image/jpeg")
errors = errors | await async_test_stream(self.hass, import_config)
# Don't bother testing the still or stream details on yaml import.
still_url = import_config.get(CONF_STILL_IMAGE_URL)
stream_url = import_config.get(CONF_STREAM_SOURCE)
name = import_config.get(
CONF_NAME, slug_url(still_url) or slug_url(stream_url) or DEFAULT_NAME
)
if CONF_LIMIT_REFETCH_TO_URL_CHANGE not in import_config:
import_config[CONF_LIMIT_REFETCH_TO_URL_CHANGE] = False
if not errors:
import_config[CONF_CONTENT_TYPE] = still_format
await self.async_set_unique_id(self.flow_id)
return self.async_create_entry(title=name, data={}, options=import_config)
_LOGGER.error(
"Error importing generic IP camera platform config: unexpected error '%s'",
list(errors.values()),
)
return self.async_abort(reason="unknown")
still_format = import_config.get(CONF_CONTENT_TYPE, "image/jpeg")
import_config[CONF_CONTENT_TYPE] = still_format
await self.async_set_unique_id(self.flow_id)
return self.async_create_entry(title=name, data={}, options=import_config)


class GenericOptionsFlowHandler(OptionsFlow):
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/generic/manifest.json
Expand Up @@ -2,7 +2,7 @@
"domain": "generic",
"name": "Generic Camera",
"config_flow": true,
"requirements": ["av==9.0.0", "pillow==9.0.1"],
"requirements": ["av==8.1.0", "pillow==9.0.1"],
"documentation": "https://www.home-assistant.io/integrations/generic",
"codeowners": ["@davet2001"],
"iot_class": "local_push"
Expand Down
22 changes: 20 additions & 2 deletions homeassistant/components/google/__init__.py
Expand Up @@ -7,6 +7,7 @@
import logging
from typing import Any

import aiohttp
from httplib2.error import ServerNotFoundError
from oauth2client.file import Storage
import voluptuous as vol
Expand All @@ -24,7 +25,11 @@
CONF_OFFSET,
)
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.exceptions import ConfigEntryAuthFailed, HomeAssistantError
from homeassistant.exceptions import (
ConfigEntryAuthFailed,
ConfigEntryNotReady,
HomeAssistantError,
)
from homeassistant.helpers import config_entry_oauth2_flow
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import async_dispatcher_send
Expand Down Expand Up @@ -185,8 +190,21 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass, entry
)
)
assert isinstance(implementation, DeviceAuth)
session = config_entry_oauth2_flow.OAuth2Session(hass, entry, implementation)
# Force a token refresh to fix a bug where tokens were persisted with
# expires_in (relative time delta) and expires_at (absolute time) swapped.
if session.token["expires_at"] >= datetime(2070, 1, 1).timestamp():
session.token["expires_in"] = 0
session.token["expires_at"] = datetime.now().timestamp()
try:
await session.async_ensure_token_valid()
except aiohttp.ClientResponseError as err:
if 400 <= err.status < 500:
raise ConfigEntryAuthFailed from err
raise ConfigEntryNotReady from err
except aiohttp.ClientError as err:
raise ConfigEntryNotReady from err

required_scope = hass.data[DOMAIN][DATA_CONFIG][CONF_CALENDAR_ACCESS].scope
if required_scope not in session.token.get("scope", []):
raise ConfigEntryAuthFailed(
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/google/config_flow.py
Expand Up @@ -34,7 +34,7 @@ def logger(self) -> logging.Logger:
return logging.getLogger(__name__)

async def async_step_import(self, info: dict[str, Any]) -> FlowResult:
"""Import existing auth from Nest."""
"""Import existing auth into a new config entry."""
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
implementations = await config_entry_oauth2_flow.async_get_implementations(
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/google/strings.json
Expand Up @@ -6,7 +6,7 @@
},
"reauth_confirm": {
"title": "[%key:common::config_flow::title::reauth%]",
"description": "The Nest integration needs to re-authenticate your account"
"description": "The Google Calendar integration needs to re-authenticate your account"
},
"auth": {
"title": "Link Google Account"
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/google/translations/en.json
Expand Up @@ -3,7 +3,7 @@
"abort": {
"already_configured": "Account is already configured",
"already_in_progress": "Configuration flow is already in progress",
"code_expired": "Authentication code expired, please try again.",
"code_expired": "Authentication code expired or credential setup is invalid, please try again.",
"invalid_access_token": "Invalid access token",
"missing_configuration": "The component is not configured. Please follow the documentation.",
"oauth_error": "Received invalid token data.",
Expand All @@ -23,7 +23,7 @@
"title": "Pick Authentication Method"
},
"reauth_confirm": {
"description": "The Nest integration needs to re-authenticate your account",
"description": "The Google Calendar integration needs to re-authenticate your account",
"title": "Reauthenticate Integration"
}
}
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/mpd/manifest.json
Expand Up @@ -2,7 +2,7 @@
"domain": "mpd",
"name": "Music Player Daemon (MPD)",
"documentation": "https://www.home-assistant.io/integrations/mpd",
"requirements": ["python-mpd2==3.0.4"],
"requirements": ["python-mpd2==3.0.5"],
"codeowners": ["@fabaff"],
"iot_class": "local_polling",
"loggers": ["mpd"]
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/netgear/switch.py
Expand Up @@ -90,10 +90,12 @@ def is_on(self):
async def async_turn_on(self, **kwargs):
"""Turn the switch on."""
await self._router.async_allow_block_device(self._mac, ALLOW)
await self.coordinator.async_request_refresh()

async def async_turn_off(self, **kwargs):
"""Turn the switch off."""
await self._router.async_allow_block_device(self._mac, BLOCK)
await self.coordinator.async_request_refresh()

@callback
def async_update_device(self) -> None:
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/nina/manifest.json
Expand Up @@ -3,7 +3,7 @@
"name": "NINA",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/nina",
"requirements": ["pynina==0.1.7"],
"requirements": ["pynina==0.1.8"],
"dependencies": [],
"codeowners": ["@DeerMaximum"],
"iot_class": "cloud_polling",
Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/plex/__init__.py
Expand Up @@ -159,7 +159,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
entry.data[CONF_SERVER],
error,
)
return False
# Retry as setups behind a proxy can return transient 404 or 502 errors
raise ConfigEntryNotReady from error

_LOGGER.debug(
"Connected to: %s (%s)", plex_server.friendly_name, plex_server.url_in_use
Expand Down
4 changes: 3 additions & 1 deletion homeassistant/components/plex/services.py
Expand Up @@ -173,7 +173,9 @@ def process_plex_payload(
media = plex_server.lookup_media(content_type, **search_query)

if supports_playqueues and (isinstance(media, list) or shuffle):
playqueue = plex_server.create_playqueue(media, shuffle=shuffle)
playqueue = plex_server.create_playqueue(
media, includeRelated=0, shuffle=shuffle
)
return PlexMediaSearchResult(playqueue, content)

return PlexMediaSearchResult(media, content)
2 changes: 1 addition & 1 deletion homeassistant/components/rtsp_to_webrtc/manifest.json
Expand Up @@ -3,7 +3,7 @@
"name": "RTSPtoWebRTC",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/rtsp_to_webrtc",
"requirements": ["rtsp-to-webrtc==0.5.0"],
"requirements": ["rtsp-to-webrtc==0.5.1"],
"dependencies": ["camera"],
"codeowners": ["@allenporter"],
"iot_class": "local_push",
Expand Down
13 changes: 11 additions & 2 deletions homeassistant/components/samsungtv/media_player.py
Expand Up @@ -214,13 +214,19 @@ async def async_update(self) -> None:
)

if self._attr_state != STATE_ON:
if self._dmr_device and self._dmr_device.is_subscribed:
await self._dmr_device.async_unsubscribe_services()
return

startup_tasks: list[Coroutine[Any, Any, None]] = []
startup_tasks: list[Coroutine[Any, Any, Any]] = []

if not self._app_list_event.is_set():
startup_tasks.append(self._async_startup_app_list())

if self._dmr_device and not self._dmr_device.is_subscribed:
startup_tasks.append(
self._dmr_device.async_subscribe_services(auto_resubscribe=True)
)
if not self._dmr_device and self._ssdp_rendering_control_location:
startup_tasks.append(self._async_startup_dmr())

Expand Down Expand Up @@ -273,7 +279,10 @@ async def _async_startup_dmr(self) -> None:
if self._dmr_device is None:
session = async_get_clientsession(self.hass)
upnp_requester = AiohttpSessionRequester(session)
upnp_factory = UpnpFactory(upnp_requester)
# Set non_strict to avoid invalid data sent by Samsung TV:
# Got invalid value for <UpnpStateVariable(PlaybackStorageMedium, string)>:
# NETWORK,NONE
upnp_factory = UpnpFactory(upnp_requester, non_strict=True)
upnp_device: UpnpDevice | None = None
with contextlib.suppress(UpnpConnectionError):
upnp_device = await upnp_factory.async_create_device(
Expand Down
3 changes: 0 additions & 3 deletions homeassistant/components/shelly/cover.py
Expand Up @@ -157,9 +157,6 @@ def __init__(self, wrapper: RpcDeviceWrapper, id_: int) -> None:
@property
def is_closed(self) -> bool | None:
"""If cover is closed."""
if not self.status["pos_control"]:
return None

return cast(bool, self.status["state"] == "closed")

@property
Expand Down
7 changes: 4 additions & 3 deletions homeassistant/components/sleepiq/number.py
Expand Up @@ -130,6 +130,7 @@ async def async_setup_entry(
class SleepIQNumberEntity(SleepIQBedEntity, NumberEntity):
"""Representation of a SleepIQ number entity."""

entity_description: SleepIQNumberEntityDescription
_attr_icon = "mdi:bed"

def __init__(
Expand All @@ -140,7 +141,7 @@ def __init__(
description: SleepIQNumberEntityDescription,
) -> None:
"""Initialize the number."""
self.description = description
self.entity_description = description
self.device = device

self._attr_name = description.get_name_fn(bed, device)
Expand All @@ -151,10 +152,10 @@ def __init__(
@callback
def _async_update_attrs(self) -> None:
"""Update number attributes."""
self._attr_value = float(self.description.value_fn(self.device))
self._attr_value = float(self.entity_description.value_fn(self.device))

async def async_set_value(self, value: float) -> None:
"""Set the number value."""
await self.description.set_value_fn(self.device, int(value))
await self.entity_description.set_value_fn(self.device, int(value))
self._attr_value = value
self.async_write_ha_state()
24 changes: 12 additions & 12 deletions homeassistant/components/soundtouch/services.yaml
Expand Up @@ -28,10 +28,10 @@ create_zone:
description: Name of slaves entities to add to the new zone.
required: true
selector:
target:
entity:
integration: soundtouch
domain: media_player
entity:
multiple: true
integration: soundtouch
domain: media_player

add_zone_slave:
name: Add zone slave
Expand All @@ -50,10 +50,10 @@ add_zone_slave:
description: Name of slaves entities to add to the existing zone.
required: true
selector:
target:
entity:
integration: soundtouch
domain: media_player
entity:
multiple: true
integration: soundtouch
domain: media_player

remove_zone_slave:
name: Remove zone slave
Expand All @@ -72,7 +72,7 @@ remove_zone_slave:
description: Name of slaves entities to remove from the existing zone.
required: true
selector:
target:
entity:
integration: soundtouch
domain: media_player
entity:
multiple: true
integration: soundtouch
domain: media_player
2 changes: 1 addition & 1 deletion homeassistant/components/stream/manifest.json
Expand Up @@ -2,7 +2,7 @@
"domain": "stream",
"name": "Stream",
"documentation": "https://www.home-assistant.io/integrations/stream",
"requirements": ["PyTurboJPEG==1.6.6", "av==9.0.0"],
"requirements": ["PyTurboJPEG==1.6.6", "av==8.1.0"],
"dependencies": ["http"],
"codeowners": ["@hunterjm", "@uvjustin", "@allenporter"],
"quality_scale": "internal",
Expand Down