Skip to content
Merged
47 changes: 29 additions & 18 deletions homeassistant/components/arcam_fmj/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

from collections.abc import Callable
from dataclasses import dataclass
import logging

from arcam.fmj import IncomingVideoAspectRatio, IncomingVideoColorspace
from arcam.fmj import IncomingVideoAspectRatio, IncomingVideoColorspace, IntOrTypeEnum
from arcam.fmj.state import IncomingAudioConfig, IncomingAudioFormat, State

from homeassistant.components.sensor import (
Expand All @@ -21,6 +22,25 @@
from .coordinator import ArcamFmjConfigEntry
from .entity import ArcamFmjEntity

_LOGGER = logging.getLogger(__name__)


def _enum_options(value: type[IntOrTypeEnum]) -> list[str]:
return [
member.name.lower() for member in value if not member.name.startswith("CODE_")
]


def _enum_value(value: IntOrTypeEnum | None) -> str | None:
if value is None:
return None

if value.name.startswith("CODE_"):
_LOGGER.debug("Undefined enum value %s ignored", value)
return None

return value.name.lower()


@dataclass(frozen=True, kw_only=True)
class ArcamFmjSensorEntityDescription(SensorEntityDescription):
Expand Down Expand Up @@ -75,9 +95,9 @@ class ArcamFmjSensorEntityDescription(SensorEntityDescription):
translation_key="incoming_video_aspect_ratio",
entity_category=EntityCategory.DIAGNOSTIC,
device_class=SensorDeviceClass.ENUM,
options=[member.name.lower() for member in IncomingVideoAspectRatio],
options=_enum_options(IncomingVideoAspectRatio),
value_fn=lambda state: (
vp.aspect_ratio.name.lower()
_enum_value(vp.aspect_ratio)
if (vp := state.get_incoming_video_parameters()) is not None
else None
),
Expand All @@ -87,11 +107,10 @@ class ArcamFmjSensorEntityDescription(SensorEntityDescription):
translation_key="incoming_video_colorspace",
entity_category=EntityCategory.DIAGNOSTIC,
device_class=SensorDeviceClass.ENUM,
options=[member.name.lower() for member in IncomingVideoColorspace],
options=_enum_options(IncomingVideoColorspace),
value_fn=lambda state: (
vp.colorspace.name.lower()
_enum_value(vp.colorspace)
if (vp := state.get_incoming_video_parameters()) is not None
and vp.colorspace is not None
else None
),
),
Expand All @@ -100,24 +119,16 @@ class ArcamFmjSensorEntityDescription(SensorEntityDescription):
translation_key="incoming_audio_format",
entity_category=EntityCategory.DIAGNOSTIC,
device_class=SensorDeviceClass.ENUM,
options=[member.name.lower() for member in IncomingAudioFormat],
value_fn=lambda state: (
result.name.lower()
if (result := state.get_incoming_audio_format()[0]) is not None
else None
),
options=_enum_options(IncomingAudioFormat),
value_fn=lambda state: _enum_value(state.get_incoming_audio_format()[0]),
),
ArcamFmjSensorEntityDescription(
key="incoming_audio_config",
translation_key="incoming_audio_config",
entity_category=EntityCategory.DIAGNOSTIC,
device_class=SensorDeviceClass.ENUM,
options=[member.name.lower() for member in IncomingAudioConfig],
value_fn=lambda state: (
result.name.lower()
if (result := state.get_incoming_audio_format()[1]) is not None
else None
),
options=_enum_options(IncomingAudioConfig),
value_fn=lambda state: _enum_value(state.get_incoming_audio_format()[1]),
),
ArcamFmjSensorEntityDescription(
key="incoming_audio_sample_rate",
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/forecast_solar/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
from .const import DOMAIN
from .coordinator import ForecastSolarDataUpdateCoordinator

PARALLEL_UPDATES = 0


@dataclass(frozen=True)
class ForecastSolarSensorEntityDescription(SensorEntityDescription):
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/fumis/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@
"iot_class": "cloud_polling",
"loggers": ["fumis"],
"quality_scale": "bronze",
"requirements": ["fumis==0.3.0"]
"requirements": ["fumis==0.4.0"]
}
2 changes: 2 additions & 0 deletions homeassistant/components/hydrawise/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
from .coordinator import HydrawiseConfigEntry
from .entity import HydrawiseEntity

PARALLEL_UPDATES = 1


@dataclass(frozen=True, kw_only=True)
class HydrawiseBinarySensorEntityDescription(BinarySensorEntityDescription):
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/hydrawise/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
from .coordinator import HydrawiseConfigEntry
from .entity import HydrawiseEntity

PARALLEL_UPDATES = 0


@dataclass(frozen=True, kw_only=True)
class HydrawiseSensorEntityDescription(SensorEntityDescription):
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/hydrawise/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
from .coordinator import HydrawiseConfigEntry
from .entity import HydrawiseEntity

PARALLEL_UPDATES = 1


@dataclass(frozen=True, kw_only=True)
class HydrawiseSwitchEntityDescription(SwitchEntityDescription):
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/hydrawise/valve.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
from .coordinator import HydrawiseConfigEntry
from .entity import HydrawiseEntity

PARALLEL_UPDATES = 1

VALVE_TYPES: tuple[ValveEntityDescription, ...] = (
ValveEntityDescription(
key="zone",
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/mealie/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"integration_type": "service",
"iot_class": "local_polling",
"quality_scale": "platinum",
"requirements": ["aiomealie==1.2.3"]
"requirements": ["aiomealie==1.2.4"]
}
27 changes: 26 additions & 1 deletion homeassistant/components/nfandroidtv/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import voluptuous as vol

from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST
from homeassistant.const import CONF_HOST, CONF_NAME

from .const import DEFAULT_NAME, DOMAIN

Expand Down Expand Up @@ -40,6 +40,31 @@ async def async_step_user(
errors=errors,
)

async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle reconfigure flow for Notification for Android TV / Fire TV."""
errors: dict[str, str] = {}
entry = self._get_reconfigure_entry()

if user_input is not None:
self._async_abort_entries_match(user_input)
if not (error := await self._async_try_connect(user_input[CONF_HOST])):
return self.async_update_reload_and_abort(
entry, data_updates=user_input
)
errors["base"] = error

return self.async_show_form(
step_id="reconfigure",
data_schema=self.add_suggested_values_to_schema(
data_schema=vol.Schema({vol.Required(CONF_HOST): str}),
suggested_values=user_input or entry.data,
),
description_placeholders={CONF_NAME: entry.title},
errors=errors,
)

async def _async_try_connect(self, host: str) -> str | None:
"""Try connecting to Android TV / Fire TV."""
try:
Expand Down
12 changes: 11 additions & 1 deletion homeassistant/components/nfandroidtv/strings.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
{
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"step": {
"reconfigure": {
"data": {
"host": "[%key:common::config_flow::data::host%]"
},
"data_description": {
"host": "[%key:component::nfandroidtv::config::step::user::data_description::host%]"
},
"description": "Reconfigure {name}"
},
"user": {
"data": {
"host": "[%key:common::config_flow::data::host%]",
Expand Down
22 changes: 8 additions & 14 deletions homeassistant/components/onvif/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""The ONVIF integration."""
# pylint: disable=hass-use-runtime-data # Uses legacy hass.data[DOMAIN] pattern

import asyncio
from contextlib import AsyncExitStack, suppress
Expand All @@ -13,7 +12,6 @@

from homeassistant.components.ffmpeg import CONF_EXTRA_ARGUMENTS
from homeassistant.components.stream import CONF_RTSP_TRANSPORT, RTSP_TRANSPORTS
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
EVENT_HOMEASSISTANT_STOP,
HTTP_BASIC_AUTHENTICATION,
Expand All @@ -29,18 +27,14 @@
CONF_SNAPSHOT_AUTH,
DEFAULT_ARGUMENTS,
DEFAULT_ENABLE_WEBHOOKS,
DOMAIN,
)
from .device import ONVIFDevice
from .device import ONVIFConfigEntry, ONVIFDevice

LOGGER = logging.getLogger(__name__)


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_setup_entry(hass: HomeAssistant, entry: ONVIFConfigEntry) -> bool:
"""Set up ONVIF from a config entry."""
if DOMAIN not in hass.data:
hass.data[DOMAIN] = {}

if not entry.options:
await async_populate_options(hass, entry)

Expand Down Expand Up @@ -97,7 +91,7 @@ async def _cleanup():
# If we get here, setup was successful - prevent cleanup
stack.pop_all()

hass.data[DOMAIN][entry.unique_id] = device
entry.runtime_data = device

device.platforms = [Platform.BUTTON, Platform.CAMERA]

Expand Down Expand Up @@ -128,9 +122,9 @@ async def _async_stop_device(hass: HomeAssistant, device: ONVIFDevice) -> None:
await device.device.close()


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_unload_entry(hass: HomeAssistant, entry: ONVIFConfigEntry) -> bool:
"""Unload a config entry."""
device: ONVIFDevice = hass.data[DOMAIN][entry.unique_id]
device = entry.runtime_data
await _async_stop_device(hass, device)
return await hass.config_entries.async_unload_platforms(entry, device.platforms)

Expand All @@ -150,7 +144,7 @@ async def _get_snapshot_auth(device: ONVIFDevice) -> str | None:


async def async_populate_snapshot_auth(
hass: HomeAssistant, device: ONVIFDevice, entry: ConfigEntry
hass: HomeAssistant, device: ONVIFDevice, entry: ONVIFConfigEntry
) -> None:
"""Check if digest auth for snapshots is possible."""
if auth := await _get_snapshot_auth(device):
Expand All @@ -159,7 +153,7 @@ async def async_populate_snapshot_auth(
)


async def async_populate_options(hass: HomeAssistant, entry: ConfigEntry) -> None:
async def async_populate_options(hass: HomeAssistant, entry: ONVIFConfigEntry) -> None:
"""Populate default options for device."""
options = {
CONF_EXTRA_ARGUMENTS: DEFAULT_ARGUMENTS,
Expand All @@ -172,7 +166,7 @@ async def async_populate_options(hass: HomeAssistant, entry: ConfigEntry) -> Non

@callback
def _async_migrate_camera_entities_unique_ids(
hass: HomeAssistant, config_entry: ConfigEntry, device: ONVIFDevice
hass: HomeAssistant, config_entry: ONVIFConfigEntry, device: ONVIFDevice
) -> None:
"""Migrate unique ids of camera entities from profile index to profile token."""
entity_reg = er.async_get(hass)
Expand Down
10 changes: 3 additions & 7 deletions homeassistant/components/onvif/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,25 @@
BinarySensorDeviceClass,
BinarySensorEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import STATE_ON
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.util.enum import try_parse_enum

from .const import DOMAIN
from .device import ONVIFDevice
from .device import ONVIFConfigEntry, ONVIFDevice
from .entity import ONVIFBaseEntity
from .util import build_event_entity_names


async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
config_entry: ONVIFConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up ONVIF binary sensor platform."""
# Uses legacy hass.data[DOMAIN] pattern
# pylint: disable-next=hass-use-runtime-data
device: ONVIFDevice = hass.data[DOMAIN][config_entry.unique_id]
device = config_entry.runtime_data

events = device.events.get_platform("binary_sensor")
entity_names = build_event_entity_names(events)
Expand Down
10 changes: 3 additions & 7 deletions homeassistant/components/onvif/button.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
"""ONVIF Buttons."""

from homeassistant.components.button import ButtonDeviceClass, ButtonEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .const import DOMAIN
from .device import ONVIFDevice
from .device import ONVIFConfigEntry, ONVIFDevice
from .entity import ONVIFBaseEntity


async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
config_entry: ONVIFConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up ONVIF button based on a config entry."""
# Uses legacy hass.data[DOMAIN] pattern
# pylint: disable-next=hass-use-runtime-data
device = hass.data[DOMAIN][config_entry.unique_id]
device = config_entry.runtime_data
async_add_entities([RebootButton(device), SetSystemDateAndTimeButton(device)])


Expand Down
Loading
Loading