mirror of
https://github.com/home-assistant/core.git
synced 2025-06-25 01:21:51 +02:00
Add config flow to AnthemAV integration (#53268)
* Add config flow to AnthemAV integration * Add importing of existing configuration * Change setting to optional and add default value * Use entity attribute * Reduce changes by removing additional media player properties * Remove title from translation * Refactor config flow and fix PR comments * Fix a failing test because of wrong renaming * Add typing and use existing class and enum * Bump dependency to v1.3.1 * Remove unecessary async_reload_entry * Fix requirements_test_all after rebase * Add const for timeout and remove async_block in test * Reapply CodeOwner and configflow after rebase * Remove name from configflow * Fix manifest prettier failure * Simplify code and avoid catching broad exception * Removed unused strings and translations * Avoid asserting hass.data
This commit is contained in:
@ -74,6 +74,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/analytics/ @home-assistant/core @ludeeus
|
||||
/homeassistant/components/androidtv/ @JeffLIrion @ollo69
|
||||
/tests/components/androidtv/ @JeffLIrion @ollo69
|
||||
/homeassistant/components/anthemav/ @hyralex
|
||||
/tests/components/anthemav/ @hyralex
|
||||
/homeassistant/components/apache_kafka/ @bachya
|
||||
/tests/components/apache_kafka/ @bachya
|
||||
/homeassistant/components/api/ @home-assistant/core
|
||||
|
@ -1 +1,59 @@
|
||||
"""The anthemav component."""
|
||||
"""The Anthem A/V Receivers integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
import anthemav
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PORT, Platform
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
|
||||
from .const import ANTHEMAV_UDATE_SIGNAL, DOMAIN
|
||||
|
||||
PLATFORMS = [Platform.MEDIA_PLAYER]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Anthem A/V Receivers from a config entry."""
|
||||
|
||||
@callback
|
||||
def async_anthemav_update_callback(message):
|
||||
"""Receive notification from transport that new data exists."""
|
||||
_LOGGER.debug("Received update callback from AVR: %s", message)
|
||||
async_dispatcher_send(hass, f"{ANTHEMAV_UDATE_SIGNAL}_{entry.data[CONF_NAME]}")
|
||||
|
||||
try:
|
||||
avr = await anthemav.Connection.create(
|
||||
host=entry.data[CONF_HOST],
|
||||
port=entry.data[CONF_PORT],
|
||||
update_callback=async_anthemav_update_callback,
|
||||
)
|
||||
|
||||
except OSError as err:
|
||||
raise ConfigEntryNotReady from err
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = avr
|
||||
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
avr = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
if avr is not None:
|
||||
_LOGGER.debug("Close avr connection")
|
||||
avr.close()
|
||||
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
return unload_ok
|
||||
|
96
homeassistant/components/anthemav/config_flow.py
Normal file
96
homeassistant/components/anthemav/config_flow.py
Normal file
@ -0,0 +1,96 @@
|
||||
"""Config flow for Anthem A/V Receivers integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
import anthemav
|
||||
from anthemav.connection import Connection
|
||||
from anthemav.device_error import DeviceError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_NAME, CONF_PORT
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.device_registry import format_mac
|
||||
|
||||
from .const import CONF_MODEL, DEFAULT_NAME, DEFAULT_PORT, DOMAIN
|
||||
|
||||
DEVICE_TIMEOUT_SECONDS = 4.0
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
STEP_USER_DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Required(CONF_PORT, default=DEFAULT_PORT): int,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def connect_device(user_input: dict[str, Any]) -> Connection:
|
||||
"""Connect to the AVR device."""
|
||||
avr = await anthemav.Connection.create(
|
||||
host=user_input[CONF_HOST], port=user_input[CONF_PORT], auto_reconnect=False
|
||||
)
|
||||
await avr.reconnect()
|
||||
await avr.protocol.wait_for_device_initialised(DEVICE_TIMEOUT_SECONDS)
|
||||
return avr
|
||||
|
||||
|
||||
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Anthem A/V Receivers."""
|
||||
|
||||
VERSION = 1
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle the initial step."""
|
||||
if user_input is None:
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=STEP_USER_DATA_SCHEMA
|
||||
)
|
||||
|
||||
if CONF_NAME not in user_input:
|
||||
user_input[CONF_NAME] = DEFAULT_NAME
|
||||
|
||||
errors = {}
|
||||
|
||||
avr: Connection | None = None
|
||||
try:
|
||||
avr = await connect_device(user_input)
|
||||
except OSError:
|
||||
_LOGGER.error(
|
||||
"Couldn't establish connection to %s:%s",
|
||||
user_input[CONF_HOST],
|
||||
user_input[CONF_PORT],
|
||||
)
|
||||
errors["base"] = "cannot_connect"
|
||||
except DeviceError:
|
||||
_LOGGER.error(
|
||||
"Couldn't receive device information from %s:%s",
|
||||
user_input[CONF_HOST],
|
||||
user_input[CONF_PORT],
|
||||
)
|
||||
errors["base"] = "cannot_receive_deviceinfo"
|
||||
else:
|
||||
user_input[CONF_MAC] = format_mac(avr.protocol.macaddress)
|
||||
user_input[CONF_MODEL] = avr.protocol.model
|
||||
await self.async_set_unique_id(user_input[CONF_MAC])
|
||||
self._abort_if_unique_id_configured()
|
||||
return self.async_create_entry(title=user_input[CONF_NAME], data=user_input)
|
||||
finally:
|
||||
if avr is not None:
|
||||
avr.close()
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
|
||||
)
|
||||
|
||||
async def async_step_import(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Import a config entry from configuration.yaml."""
|
||||
return await self.async_step_user(user_input)
|
7
homeassistant/components/anthemav/const.py
Normal file
7
homeassistant/components/anthemav/const.py
Normal file
@ -0,0 +1,7 @@
|
||||
"""Constants for the Anthem A/V Receivers integration."""
|
||||
ANTHEMAV_UDATE_SIGNAL = "anthemav_update"
|
||||
CONF_MODEL = "model"
|
||||
DEFAULT_NAME = "Anthem AV"
|
||||
DEFAULT_PORT = 14999
|
||||
DOMAIN = "anthemav"
|
||||
MANUFACTURER = "Anthem"
|
@ -2,8 +2,9 @@
|
||||
"domain": "anthemav",
|
||||
"name": "Anthem A/V Receivers",
|
||||
"documentation": "https://www.home-assistant.io/integrations/anthemav",
|
||||
"requirements": ["anthemav==1.2.0"],
|
||||
"codeowners": [],
|
||||
"requirements": ["anthemav==1.3.2"],
|
||||
"codeowners": ["@hyralex"],
|
||||
"config_flow": true,
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["anthemav"]
|
||||
}
|
||||
|
@ -2,8 +2,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
import anthemav
|
||||
from anthemav.connection import Connection
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.media_player import (
|
||||
@ -11,33 +12,37 @@ from homeassistant.components.media_player import (
|
||||
MediaPlayerEntity,
|
||||
MediaPlayerEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_HOST,
|
||||
CONF_MAC,
|
||||
CONF_NAME,
|
||||
CONF_PORT,
|
||||
EVENT_HOMEASSISTANT_STOP,
|
||||
STATE_OFF,
|
||||
STATE_ON,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.core import HomeAssistant
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.dispatcher import (
|
||||
async_dispatcher_connect,
|
||||
async_dispatcher_send,
|
||||
)
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from .const import (
|
||||
ANTHEMAV_UDATE_SIGNAL,
|
||||
CONF_MODEL,
|
||||
DEFAULT_NAME,
|
||||
DEFAULT_PORT,
|
||||
DOMAIN,
|
||||
MANUFACTURER,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DOMAIN = "anthemav"
|
||||
|
||||
DEFAULT_PORT = 14999
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Optional(CONF_NAME): cv.string,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
|
||||
}
|
||||
)
|
||||
@ -50,30 +55,33 @@ async def async_setup_platform(
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> None:
|
||||
"""Set up our socket to the AVR."""
|
||||
|
||||
host = config[CONF_HOST]
|
||||
port = config[CONF_PORT]
|
||||
name = config.get(CONF_NAME)
|
||||
device = None
|
||||
|
||||
_LOGGER.info("Provisioning Anthem AVR device at %s:%d", host, port)
|
||||
|
||||
@callback
|
||||
def async_anthemav_update_callback(message):
|
||||
"""Receive notification from transport that new data exists."""
|
||||
_LOGGER.debug("Received update callback from AVR: %s", message)
|
||||
async_dispatcher_send(hass, DOMAIN)
|
||||
|
||||
avr = await anthemav.Connection.create(
|
||||
host=host, port=port, update_callback=async_anthemav_update_callback
|
||||
_LOGGER.warning(
|
||||
"AnthemAV configuration is deprecated and has been automatically imported. Please remove the integration from your configuration file"
|
||||
)
|
||||
await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data=config,
|
||||
)
|
||||
|
||||
device = AnthemAVR(avr, name)
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up entry."""
|
||||
name = config_entry.data[CONF_NAME]
|
||||
macaddress = config_entry.data[CONF_MAC]
|
||||
model = config_entry.data[CONF_MODEL]
|
||||
|
||||
avr = hass.data[DOMAIN][config_entry.entry_id]
|
||||
|
||||
device = AnthemAVR(avr, name, macaddress, model)
|
||||
|
||||
_LOGGER.debug("dump_devicedata: %s", device.dump_avrdata)
|
||||
_LOGGER.debug("dump_conndata: %s", avr.dump_conndata)
|
||||
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, device.avr.close)
|
||||
async_add_entities([device])
|
||||
|
||||
|
||||
@ -89,23 +97,34 @@ class AnthemAVR(MediaPlayerEntity):
|
||||
| MediaPlayerEntityFeature.SELECT_SOURCE
|
||||
)
|
||||
|
||||
def __init__(self, avr, name):
|
||||
def __init__(self, avr: Connection, name: str, macaddress: str, model: str) -> None:
|
||||
"""Initialize entity with transport."""
|
||||
super().__init__()
|
||||
self.avr = avr
|
||||
self._attr_name = name or self._lookup("model")
|
||||
self._attr_name = name
|
||||
self._attr_unique_id = macaddress
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, macaddress)},
|
||||
name=name,
|
||||
manufacturer=MANUFACTURER,
|
||||
model=model,
|
||||
)
|
||||
|
||||
def _lookup(self, propname, dval=None):
|
||||
def _lookup(self, propname: str, dval: Any | None = None) -> Any | None:
|
||||
return getattr(self.avr.protocol, propname, dval)
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""When entity is added to hass."""
|
||||
self.async_on_remove(
|
||||
async_dispatcher_connect(self.hass, DOMAIN, self.async_write_ha_state)
|
||||
async_dispatcher_connect(
|
||||
self.hass,
|
||||
f"{ANTHEMAV_UDATE_SIGNAL}_{self._attr_name}",
|
||||
self.async_write_ha_state,
|
||||
)
|
||||
)
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
def state(self) -> str | None:
|
||||
"""Return state of power on/off."""
|
||||
pwrstate = self._lookup("power")
|
||||
|
||||
@ -116,22 +135,22 @@ class AnthemAVR(MediaPlayerEntity):
|
||||
return None
|
||||
|
||||
@property
|
||||
def is_volume_muted(self):
|
||||
def is_volume_muted(self) -> bool | None:
|
||||
"""Return boolean reflecting mute state on device."""
|
||||
return self._lookup("mute", False)
|
||||
|
||||
@property
|
||||
def volume_level(self):
|
||||
def volume_level(self) -> float | None:
|
||||
"""Return volume level from 0 to 1."""
|
||||
return self._lookup("volume_as_percentage", 0.0)
|
||||
|
||||
@property
|
||||
def media_title(self):
|
||||
def media_title(self) -> str | None:
|
||||
"""Return current input name (closest we have to media title)."""
|
||||
return self._lookup("input_name", "No Source")
|
||||
|
||||
@property
|
||||
def app_name(self):
|
||||
def app_name(self) -> str | None:
|
||||
"""Return details about current video and audio stream."""
|
||||
return (
|
||||
f"{self._lookup('video_input_resolution_text', '')} "
|
||||
@ -139,38 +158,38 @@ class AnthemAVR(MediaPlayerEntity):
|
||||
)
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
def source(self) -> str | None:
|
||||
"""Return currently selected input."""
|
||||
return self._lookup("input_name", "Unknown")
|
||||
|
||||
@property
|
||||
def source_list(self):
|
||||
def source_list(self) -> list[str] | None:
|
||||
"""Return all active, configured inputs."""
|
||||
return self._lookup("input_list", ["Unknown"])
|
||||
|
||||
async def async_select_source(self, source):
|
||||
async def async_select_source(self, source: str) -> None:
|
||||
"""Change AVR to the designated source (by name)."""
|
||||
self._update_avr("input_name", source)
|
||||
|
||||
async def async_turn_off(self):
|
||||
async def async_turn_off(self) -> None:
|
||||
"""Turn AVR power off."""
|
||||
self._update_avr("power", False)
|
||||
|
||||
async def async_turn_on(self):
|
||||
async def async_turn_on(self) -> None:
|
||||
"""Turn AVR power on."""
|
||||
self._update_avr("power", True)
|
||||
|
||||
async def async_set_volume_level(self, volume):
|
||||
async def async_set_volume_level(self, volume: float) -> None:
|
||||
"""Set AVR volume (0 to 1)."""
|
||||
self._update_avr("volume_as_percentage", volume)
|
||||
|
||||
async def async_mute_volume(self, mute):
|
||||
async def async_mute_volume(self, mute: bool) -> None:
|
||||
"""Engage AVR mute."""
|
||||
self._update_avr("mute", mute)
|
||||
|
||||
def _update_avr(self, propname, value):
|
||||
def _update_avr(self, propname: str, value: Any | None) -> None:
|
||||
"""Update a property in the AVR."""
|
||||
_LOGGER.info("Sending command to AVR: set %s to %s", propname, str(value))
|
||||
_LOGGER.debug("Sending command to AVR: set %s to %s", propname, str(value))
|
||||
setattr(self.avr.protocol, propname, value)
|
||||
|
||||
@property
|
||||
|
19
homeassistant/components/anthemav/strings.json
Normal file
19
homeassistant/components/anthemav/strings.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
"port": "[%key:common::config_flow::data::port%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"cannot_receive_deviceinfo": "Failed to retreive MAC Address. Make sure the device is turned on"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
}
|
||||
}
|
||||
}
|
19
homeassistant/components/anthemav/translations/en.json
Normal file
19
homeassistant/components/anthemav/translations/en.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Device is already configured"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Failed to connect",
|
||||
"cannot_receive_deviceinfo": "Failed to retreive MAC Address. Make sure the device is turned on"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Host",
|
||||
"port": "Port"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
homeassistant/components/anthemav/translations/fr.json
Normal file
19
homeassistant/components/anthemav/translations/fr.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"cannot_receive_deviceinfo": "Erreur lors de la d\u00e9couverte de l'addresse MAC. V\u00e9rifiez que l'appareil est allum\u00e9."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "H\u00f4te",
|
||||
"port": "Port"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -29,6 +29,7 @@ FLOWS = {
|
||||
"ambiclimate",
|
||||
"ambient_station",
|
||||
"androidtv",
|
||||
"anthemav",
|
||||
"apple_tv",
|
||||
"arcam_fmj",
|
||||
"aseko_pool_live",
|
||||
|
@ -310,7 +310,7 @@ androidtv[async]==0.0.67
|
||||
anel_pwrctrl-homeassistant==0.0.1.dev2
|
||||
|
||||
# homeassistant.components.anthemav
|
||||
anthemav==1.2.0
|
||||
anthemav==1.3.2
|
||||
|
||||
# homeassistant.components.apcupsd
|
||||
apcaccess==0.0.13
|
||||
|
@ -269,6 +269,9 @@ ambiclimate==0.2.1
|
||||
# homeassistant.components.androidtv
|
||||
androidtv[async]==0.0.67
|
||||
|
||||
# homeassistant.components.anthemav
|
||||
anthemav==1.3.2
|
||||
|
||||
# homeassistant.components.apprise
|
||||
apprise==0.9.9
|
||||
|
||||
|
1
tests/components/anthemav/__init__.py
Normal file
1
tests/components/anthemav/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
"""Tests for the Anthem A/V Receivers integration."""
|
28
tests/components/anthemav/conftest.py
Normal file
28
tests/components/anthemav/conftest.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""Fixtures for anthemav integration tests."""
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_anthemav() -> AsyncMock:
|
||||
"""Return the default mocked anthemav."""
|
||||
avr = AsyncMock()
|
||||
avr.protocol.macaddress = "000000000001"
|
||||
avr.protocol.model = "MRX 520"
|
||||
avr.reconnect = AsyncMock()
|
||||
avr.close = MagicMock()
|
||||
avr.protocol.input_list = []
|
||||
avr.protocol.audio_listening_mode_list = []
|
||||
return avr
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_connection_create(mock_anthemav: AsyncMock) -> AsyncMock:
|
||||
"""Return the default mocked connection.create."""
|
||||
|
||||
with patch(
|
||||
"anthemav.Connection.create",
|
||||
return_value=mock_anthemav,
|
||||
) as mock:
|
||||
yield mock
|
115
tests/components/anthemav/test_config_flow.py
Normal file
115
tests/components/anthemav/test_config_flow.py
Normal file
@ -0,0 +1,115 @@
|
||||
"""Test the Anthem A/V Receivers config flow."""
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from anthemav.device_error import DeviceError
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.anthemav.const import DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import RESULT_TYPE_CREATE_ENTRY, RESULT_TYPE_FORM
|
||||
|
||||
|
||||
async def test_form_with_valid_connection(
|
||||
hass: HomeAssistant, mock_connection_create: AsyncMock, mock_anthemav: AsyncMock
|
||||
) -> None:
|
||||
"""Test we get the form."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["errors"] is None
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.anthemav.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
"host": "1.1.1.1",
|
||||
"port": 14999,
|
||||
},
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result2["data"] == {
|
||||
"host": "1.1.1.1",
|
||||
"port": 14999,
|
||||
"name": "Anthem AV",
|
||||
"mac": "00:00:00:00:00:01",
|
||||
"model": "MRX 520",
|
||||
}
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_form_device_info_error(hass: HomeAssistant) -> None:
|
||||
"""Test we handle DeviceError from library."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
with patch(
|
||||
"anthemav.Connection.create",
|
||||
side_effect=DeviceError,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
"host": "1.1.1.1",
|
||||
"port": 14999,
|
||||
},
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == RESULT_TYPE_FORM
|
||||
assert result2["errors"] == {"base": "cannot_receive_deviceinfo"}
|
||||
|
||||
|
||||
async def test_form_cannot_connect(hass: HomeAssistant) -> None:
|
||||
"""Test we handle cannot connect error."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
with patch(
|
||||
"anthemav.Connection.create",
|
||||
side_effect=OSError,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
"host": "1.1.1.1",
|
||||
"port": 14999,
|
||||
},
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == RESULT_TYPE_FORM
|
||||
assert result2["errors"] == {"base": "cannot_connect"}
|
||||
|
||||
|
||||
async def test_import_configuration(
|
||||
hass: HomeAssistant, mock_connection_create: AsyncMock, mock_anthemav: AsyncMock
|
||||
) -> None:
|
||||
"""Test we import existing configuration."""
|
||||
config = {
|
||||
"host": "1.1.1.1",
|
||||
"port": 14999,
|
||||
"name": "Anthem Av Import",
|
||||
}
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data=config
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["data"] == {
|
||||
"host": "1.1.1.1",
|
||||
"port": 14999,
|
||||
"name": "Anthem Av Import",
|
||||
"mac": "00:00:00:00:00:01",
|
||||
"model": "MRX 520",
|
||||
}
|
65
tests/components/anthemav/test_init.py
Normal file
65
tests/components/anthemav/test_init.py
Normal file
@ -0,0 +1,65 @@
|
||||
"""Test the Anthem A/V Receivers config flow."""
|
||||
from unittest.mock import ANY, AsyncMock, patch
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.anthemav.const import CONF_MODEL, DOMAIN
|
||||
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_NAME, CONF_PORT
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_load_unload_config_entry(
|
||||
hass: HomeAssistant, mock_connection_create: AsyncMock, mock_anthemav: AsyncMock
|
||||
) -> None:
|
||||
"""Test load and unload AnthemAv component."""
|
||||
mock_config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_PORT: 14999,
|
||||
CONF_NAME: "Anthem AV",
|
||||
CONF_MAC: "aabbccddeeff",
|
||||
CONF_MODEL: "MRX 520",
|
||||
},
|
||||
)
|
||||
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# assert avr is created
|
||||
mock_connection_create.assert_called_with(
|
||||
host="1.1.1.1", port=14999, update_callback=ANY
|
||||
)
|
||||
assert mock_config_entry.state == config_entries.ConfigEntryState.LOADED
|
||||
|
||||
# unload
|
||||
await hass.config_entries.async_unload(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
# assert unload and avr is closed
|
||||
assert mock_config_entry.state == config_entries.ConfigEntryState.NOT_LOADED
|
||||
mock_anthemav.close.assert_called_once()
|
||||
|
||||
|
||||
async def test_config_entry_not_ready(hass: HomeAssistant) -> None:
|
||||
"""Test AnthemAV configuration entry not ready."""
|
||||
mock_config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_PORT: 14999,
|
||||
CONF_NAME: "Anthem AV",
|
||||
CONF_MAC: "aabbccddeeff",
|
||||
CONF_MODEL: "MRX 520",
|
||||
},
|
||||
)
|
||||
|
||||
with patch(
|
||||
"anthemav.Connection.create",
|
||||
side_effect=OSError,
|
||||
):
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_config_entry.state is config_entries.ConfigEntryState.SETUP_RETRY
|
Reference in New Issue
Block a user