Skip to content

Commit

Permalink
Merge pull request home-assistant#70054 from home-assistant/rc
Browse files Browse the repository at this point in the history
  • Loading branch information
balloob authored Apr 14, 2022
2 parents 398c7be + a537534 commit 30db51a
Show file tree
Hide file tree
Showing 28 changed files with 399 additions and 105 deletions.
8 changes: 8 additions & 0 deletions homeassistant/components/ambient_station/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
TYPE_BATT7 = "batt7"
TYPE_BATT8 = "batt8"
TYPE_BATT9 = "batt9"
TYPE_BATTIN = "battin"
TYPE_BATTOUT = "battout"
TYPE_BATT_CO2 = "batt_co2"
TYPE_BATT_LIGHTNING = "batt_lightning"
Expand Down Expand Up @@ -140,6 +141,13 @@ class AmbientBinarySensorDescription(
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATTIN,
name="Interior Battery",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT10,
name="Soil Monitor Battery 10",
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/backup/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def _read_backups(self) -> dict[str, Backup]:
size=round(backup_path.stat().st_size / 1_048_576, 2),
)
backups[backup.slug] = backup
except (OSError, TarError, json.JSONDecodeError) as err:
except (OSError, TarError, json.JSONDecodeError, KeyError) as err:
LOGGER.warning("Unable to read backup %s: %s", backup_path, err)
return backups

Expand Down
14 changes: 10 additions & 4 deletions homeassistant/components/dlna_dmr/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,16 @@ async def async_step_ssdp(self, discovery_info: ssdp.SsdpServiceInfo) -> FlowRes
discovery_service_list = discovery_info.upnp.get(ssdp.ATTR_UPNP_SERVICE_LIST)
if not discovery_service_list:
return self.async_abort(reason="not_dmr")
discovery_service_ids = {
service.get("serviceId")
for service in discovery_service_list.get("service") or []
}

services = discovery_service_list.get("service")
if not services:
discovery_service_ids: set[str] = set()
elif isinstance(services, list):
discovery_service_ids = {service.get("serviceId") for service in services}
else:
# Only one service defined (etree_to_dict failed to make a list)
discovery_service_ids = {services.get("serviceId")}

if not DmrDevice.SERVICE_IDS.issubset(discovery_service_ids):
return self.async_abort(reason="not_dmr")

Expand Down
14 changes: 10 additions & 4 deletions homeassistant/components/dlna_dms/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,16 @@ async def async_step_ssdp(self, discovery_info: ssdp.SsdpServiceInfo) -> FlowRes
discovery_service_list = discovery_info.upnp.get(ssdp.ATTR_UPNP_SERVICE_LIST)
if not discovery_service_list:
return self.async_abort(reason="not_dms")
discovery_service_ids = {
service.get("serviceId")
for service in discovery_service_list.get("service") or []
}

services = discovery_service_list.get("service")
if not services:
discovery_service_ids: set[str] = set()
elif isinstance(services, list):
discovery_service_ids = {service.get("serviceId") for service in services}
else:
# Only one service defined (etree_to_dict failed to make a list)
discovery_service_ids = {services.get("serviceId")}

if not DmsDevice.SERVICE_IDS.issubset(discovery_service_ids):
return self.async_abort(reason="not_dms")

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/generic/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"domain": "generic",
"name": "Generic Camera",
"config_flow": true,
"requirements": ["av==8.1.0", "pillow==9.0.1"],
"requirements": ["ha-av==9.1.1-3", "pillow==9.0.1"],
"documentation": "https://www.home-assistant.io/integrations/generic",
"codeowners": ["@davet2001"],
"iot_class": "local_push"
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/hassio/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def available(self) -> bool:
"""Return True if entity is available."""
return (
super().available
and DATA_KEY_OS in self.coordinator.data
and DATA_KEY_SUPERVISOR in self.coordinator.data
and self.entity_description.key
in self.coordinator.data[DATA_KEY_SUPERVISOR]
)
Expand Down
1 change: 0 additions & 1 deletion homeassistant/components/home_connect/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,6 @@ class Dishwasher(
"""Dishwasher class."""

PROGRAMS = [
{"name": "Dishcare.Dishwasher.Program.PreRinse"},
{"name": "Dishcare.Dishwasher.Program.Auto1"},
{"name": "Dishcare.Dishwasher.Program.Auto2"},
{"name": "Dishcare.Dishwasher.Program.Auto3"},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def is_on(self) -> bool:
class HomeKitCarbonMonoxideSensor(HomeKitEntity, BinarySensorEntity):
"""Representation of a Homekit BO sensor."""

_attr_device_class = BinarySensorDeviceClass.GAS
_attr_device_class = BinarySensorDeviceClass.CO

def get_characteristic_types(self) -> list[str]:
"""Define the homekit characteristics the entity is tracking."""
Expand Down
6 changes: 5 additions & 1 deletion homeassistant/components/homekit_controller/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,10 @@ async def async_step_zeroconf(
self._abort_if_unique_id_configured(updates=updated_ip_port)

for progress in self._async_in_progress(include_uninitialized=True):
if progress["context"].get("unique_id") == normalized_hkid:
context = progress["context"]
if context.get("unique_id") == normalized_hkid and not context.get(
"pairing"
):
if paired:
# If the device gets paired, we want to dismiss
# an existing discovery since we can no longer
Expand Down Expand Up @@ -350,6 +353,7 @@ async def async_step_pair(self, pair_info=None):
await self._async_setup_controller()

if pair_info and self.finish_pairing:
self.context["pairing"] = True
code = pair_info["pairing_code"]
try:
code = ensure_pin_format(
Expand Down
28 changes: 28 additions & 0 deletions homeassistant/components/media_player/const.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""Provides the constants needed for component."""
from enum import IntEnum

# How long our auth signature on the content should be valid for
CONTENT_AUTH_EXPIRY_TIME = 3600 * 24

Expand Down Expand Up @@ -90,6 +92,32 @@
REPEAT_MODE_ONE = "one"
REPEAT_MODES = [REPEAT_MODE_OFF, REPEAT_MODE_ALL, REPEAT_MODE_ONE]


class MediaPlayerEntityFeature(IntEnum):
"""Supported features of the media player entity."""

PAUSE = 1
SEEK = 2
VOLUME_SET = 4
VOLUME_MUTE = 8
PREVIOUS_TRACK = 16
NEXT_TRACK = 32

TURN_ON = 128
TURN_OFF = 256
PLAY_MEDIA = 512
VOLUME_STEP = 1024
SELECT_SOURCE = 2048
STOP = 4096
CLEAR_PLAYLIST = 8192
PLAY = 16384
SHUFFLE_SET = 32768
SELECT_SOUND_MODE = 65536
BROWSE_MEDIA = 131072
REPEAT_SET = 262144
GROUPING = 524288


SUPPORT_PAUSE = 1
SUPPORT_SEEK = 2
SUPPORT_VOLUME_SET = 4
Expand Down
65 changes: 48 additions & 17 deletions homeassistant/components/media_player/reproduce_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from typing import Any

from homeassistant.const import (
ATTR_SUPPORTED_FEATURES,
SERVICE_MEDIA_PAUSE,
SERVICE_MEDIA_PLAY,
SERVICE_MEDIA_STOP,
Expand Down Expand Up @@ -33,6 +34,7 @@
SERVICE_PLAY_MEDIA,
SERVICE_SELECT_SOUND_MODE,
SERVICE_SELECT_SOURCE,
MediaPlayerEntityFeature,
)

# mypy: allow-untyped-defs
Expand All @@ -46,6 +48,8 @@ async def _async_reproduce_states(
reproduce_options: dict[str, Any] | None = None,
) -> None:
"""Reproduce component states."""
cur_state = hass.states.get(state.entity_id)
features = cur_state.attributes[ATTR_SUPPORTED_FEATURES] if cur_state else 0

async def call_service(service: str, keys: Iterable) -> None:
"""Call service with set of attributes given."""
Expand All @@ -59,47 +63,74 @@ async def call_service(service: str, keys: Iterable) -> None:
)

if state.state == STATE_OFF:
await call_service(SERVICE_TURN_OFF, [])
if features & MediaPlayerEntityFeature.TURN_OFF:
await call_service(SERVICE_TURN_OFF, [])
# entities that are off have no other attributes to restore
return

if state.state in (
STATE_ON,
STATE_PLAYING,
STATE_IDLE,
STATE_PAUSED,
if (
state.state
in (
STATE_ON,
STATE_PLAYING,
STATE_IDLE,
STATE_PAUSED,
)
and features & MediaPlayerEntityFeature.TURN_ON
):
await call_service(SERVICE_TURN_ON, [])

if ATTR_MEDIA_VOLUME_LEVEL in state.attributes:
cur_state = hass.states.get(state.entity_id)
features = cur_state.attributes[ATTR_SUPPORTED_FEATURES] if cur_state else 0

if (
ATTR_MEDIA_VOLUME_LEVEL in state.attributes
and features & MediaPlayerEntityFeature.VOLUME_SET
):
await call_service(SERVICE_VOLUME_SET, [ATTR_MEDIA_VOLUME_LEVEL])

if ATTR_MEDIA_VOLUME_MUTED in state.attributes:
if (
ATTR_MEDIA_VOLUME_MUTED in state.attributes
and features & MediaPlayerEntityFeature.VOLUME_MUTE
):
await call_service(SERVICE_VOLUME_MUTE, [ATTR_MEDIA_VOLUME_MUTED])

if ATTR_INPUT_SOURCE in state.attributes:
if (
ATTR_INPUT_SOURCE in state.attributes
and features & MediaPlayerEntityFeature.SELECT_SOURCE
):
await call_service(SERVICE_SELECT_SOURCE, [ATTR_INPUT_SOURCE])

if ATTR_SOUND_MODE in state.attributes:
if (
ATTR_SOUND_MODE in state.attributes
and features & MediaPlayerEntityFeature.SELECT_SOUND_MODE
):
await call_service(SERVICE_SELECT_SOUND_MODE, [ATTR_SOUND_MODE])

already_playing = False

if (ATTR_MEDIA_CONTENT_TYPE in state.attributes) and (
ATTR_MEDIA_CONTENT_ID in state.attributes
):
await call_service(
SERVICE_PLAY_MEDIA,
[ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_ENQUEUE],
)
if features & MediaPlayerEntityFeature.PLAY_MEDIA:
await call_service(
SERVICE_PLAY_MEDIA,
[ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_ENQUEUE],
)
already_playing = True

if state.state == STATE_PLAYING and not already_playing:
if (
not already_playing
and state.state == STATE_PLAYING
and features & MediaPlayerEntityFeature.PLAY
):
await call_service(SERVICE_MEDIA_PLAY, [])
elif state.state == STATE_IDLE:
await call_service(SERVICE_MEDIA_STOP, [])
if features & MediaPlayerEntityFeature.STOP:
await call_service(SERVICE_MEDIA_STOP, [])
elif state.state == STATE_PAUSED:
await call_service(SERVICE_MEDIA_PAUSE, [])
if features & MediaPlayerEntityFeature.PAUSE:
await call_service(SERVICE_MEDIA_PAUSE, [])


async def async_reproduce_states(
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/openhome/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"domain": "openhome",
"name": "Linn / OpenHome",
"documentation": "https://www.home-assistant.io/integrations/openhome",
"requirements": ["openhomedevice==2.0.1"],
"requirements": ["openhomedevice==2.0.2"],
"codeowners": ["@bazwilliams"],
"iot_class": "local_polling",
"loggers": ["async_upnp_client", "openhomedevice"]
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/prosegur/alarm_control_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def __init__(self, contract: str, auth: Auth) -> None:
self.contract = contract
self._auth = auth

self._attr_code_arm_required = False
self._attr_name = f"contract {self.contract}"
self._attr_unique_id = self.contract
self._attr_supported_features = SUPPORT_ALARM_ARM_AWAY | SUPPORT_ALARM_ARM_HOME
Expand Down
11 changes: 7 additions & 4 deletions homeassistant/components/samsungtv/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,9 +224,7 @@ async def async_update(self) -> None:
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)
)
startup_tasks.append(self._async_resubscribe_dmr())
if not self._dmr_device and self._ssdp_rendering_control_location:
startup_tasks.append(self._async_startup_dmr())

Expand Down Expand Up @@ -284,7 +282,7 @@ async def _async_startup_dmr(self) -> None:
# NETWORK,NONE
upnp_factory = UpnpFactory(upnp_requester, non_strict=True)
upnp_device: UpnpDevice | None = None
with contextlib.suppress(UpnpConnectionError):
with contextlib.suppress(UpnpConnectionError, UpnpResponseError):
upnp_device = await upnp_factory.async_create_device(
self._ssdp_rendering_control_location
)
Expand Down Expand Up @@ -319,6 +317,11 @@ async def _async_startup_dmr(self) -> None:
LOGGER.debug("Error while subscribing during device connect: %r", err)
raise

async def _async_resubscribe_dmr(self) -> None:
assert self._dmr_device
with contextlib.suppress(UpnpConnectionError):
await self._dmr_device.async_subscribe_services(auto_resubscribe=True)

async def _async_shutdown_dmr(self) -> None:
"""Handle removal."""
if (dmr_device := self._dmr_device) is not None:
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/stream/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"domain": "stream",
"name": "Stream",
"documentation": "https://www.home-assistant.io/integrations/stream",
"requirements": ["PyTurboJPEG==1.6.6", "av==8.1.0"],
"requirements": ["PyTurboJPEG==1.6.6", "ha-av==9.1.1-3"],
"dependencies": ["http"],
"codeowners": ["@hunterjm", "@uvjustin", "@allenporter"],
"quality_scale": "internal",
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/tomorrowio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
entry.data[CONF_API_KEY],
entry.data[CONF_LOCATION][CONF_LATITUDE],
entry.data[CONF_LOCATION][CONF_LONGITUDE],
unit_system="metric",
session=async_get_clientsession(hass),
)

Expand Down
Loading

0 comments on commit 30db51a

Please # to comment.