mirror of
https://github.com/home-assistant/core.git
synced 2025-07-13 16:27:08 +00:00
Update media_player and add tests to qualify vizio integration for platinum quality score (#31187)
* add media player test and update media player logic to qualify vizio for platinum quality score * add SCAN_INTERVAL and log message once when device goes from available to unavailable and vice versa * move test constants into one file to avoid defining dupes in each test file * move constant to shrink diff * move pytest fixtures to conftest.py * remove commented out code * move unload test to test_init * updates to tests and logging based on review * bypass notification service * add fixture so component setup makes it through config flow * split failure tests into two * fix setup and entity check for test_init and setup failures in test_media_player * make domain references consistent across test files * remove logging steps that were introduced to help debug * move common patches out to new fixture and use config entry everywhere appropriate * fix docstring * add test for update options to increase code coverage * add one more assert * refactor test_media_player to move boiler plate logic out of each test into _test_init function * final refactor of test_media_player to move repeat logic into separate function * update docstrings * refactor setup failure tests to move shared logic into private function * fix last new functions code to use variable instead of static config variable * remove trailing comma * test that volume_step gets properly passed to Vizio volume function * fix comment language * assert with unittest.mock.call in _test_service and use config_entries.async_setup instead of config_entries.async_add * replace config_entries.async_add with config_entries.async_setup everywhere * simplify if statement for argument assertion * fix logging based on style guide * remove accidentally committed changes * update scan interval to something more reasonable and add tests for availability changes * change filter function to list comprehension, simplify log messages, remove default entity id from logs since it is user configurable, fix docstrings
This commit is contained in:
parent
9ab6d08b97
commit
73ea34e417
@ -782,9 +782,6 @@ omit =
|
|||||||
homeassistant/components/viaggiatreno/sensor.py
|
homeassistant/components/viaggiatreno/sensor.py
|
||||||
homeassistant/components/vicare/*
|
homeassistant/components/vicare/*
|
||||||
homeassistant/components/vivotek/camera.py
|
homeassistant/components/vivotek/camera.py
|
||||||
homeassistant/components/vizio/__init__.py
|
|
||||||
homeassistant/components/vizio/const.py
|
|
||||||
homeassistant/components/vizio/media_player.py
|
|
||||||
homeassistant/components/vlc/media_player.py
|
homeassistant/components/vlc/media_player.py
|
||||||
homeassistant/components/vlc_telnet/media_player.py
|
homeassistant/components/vlc_telnet/media_player.py
|
||||||
homeassistant/components/volkszaehler/sensor.py
|
homeassistant/components/volkszaehler/sensor.py
|
||||||
|
@ -6,5 +6,6 @@
|
|||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
"codeowners": ["@raman325"],
|
"codeowners": ["@raman325"],
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"zeroconf": ["_viziocast._tcp.local."]
|
"zeroconf": ["_viziocast._tcp.local."],
|
||||||
|
"quality_scale": "platinum"
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
"""Vizio SmartCast Device support."""
|
"""Vizio SmartCast Device support."""
|
||||||
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
from typing import Callable, List
|
from typing import Callable, List
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ from .const import (
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
SCAN_INTERVAL = timedelta(seconds=10)
|
||||||
PARALLEL_UPDATES = 0
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
|
|
||||||
@ -55,13 +56,13 @@ async def async_setup_entry(
|
|||||||
device_class = config_entry.data[CONF_DEVICE_CLASS]
|
device_class = config_entry.data[CONF_DEVICE_CLASS]
|
||||||
|
|
||||||
# If config entry options not set up, set them up, otherwise assign values managed in options
|
# If config entry options not set up, set them up, otherwise assign values managed in options
|
||||||
|
volume_step = config_entry.options.get(
|
||||||
|
CONF_VOLUME_STEP, config_entry.data.get(CONF_VOLUME_STEP, DEFAULT_VOLUME_STEP),
|
||||||
|
)
|
||||||
if not config_entry.options:
|
if not config_entry.options:
|
||||||
volume_step = config_entry.data.get(CONF_VOLUME_STEP, DEFAULT_VOLUME_STEP)
|
|
||||||
hass.config_entries.async_update_entry(
|
hass.config_entries.async_update_entry(
|
||||||
config_entry, options={CONF_VOLUME_STEP: volume_step}
|
config_entry, options={CONF_VOLUME_STEP: volume_step}
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
volume_step = config_entry.options[CONF_VOLUME_STEP]
|
|
||||||
|
|
||||||
device = VizioAsync(
|
device = VizioAsync(
|
||||||
DEVICE_ID,
|
DEVICE_ID,
|
||||||
@ -74,18 +75,7 @@ async def async_setup_entry(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if not await device.can_connect():
|
if not await device.can_connect():
|
||||||
fail_auth_msg = ""
|
_LOGGER.warning("Failed to connect to %s", host)
|
||||||
if token:
|
|
||||||
fail_auth_msg = f"and auth token '{token}' are correct."
|
|
||||||
else:
|
|
||||||
fail_auth_msg = "is correct."
|
|
||||||
_LOGGER.warning(
|
|
||||||
"Failed to connect to Vizio device, please check if host '%s' "
|
|
||||||
"is valid and available. Also check if device class '%s' %s",
|
|
||||||
host,
|
|
||||||
device_class,
|
|
||||||
fail_auth_msg,
|
|
||||||
)
|
|
||||||
raise PlatformNotReady
|
raise PlatformNotReady
|
||||||
|
|
||||||
entity = VizioDevice(config_entry, device, name, volume_step, device_class)
|
entity = VizioDevice(config_entry, device, name, volume_step, device_class)
|
||||||
@ -127,10 +117,18 @@ class VizioDevice(MediaPlayerDevice):
|
|||||||
is_on = await self._device.get_power_state(log_api_exception=False)
|
is_on = await self._device.get_power_state(log_api_exception=False)
|
||||||
|
|
||||||
if is_on is None:
|
if is_on is None:
|
||||||
self._available = False
|
if self._available:
|
||||||
|
_LOGGER.warning(
|
||||||
|
"Lost connection to %s", self._config_entry.data[CONF_HOST]
|
||||||
|
)
|
||||||
|
self._available = False
|
||||||
return
|
return
|
||||||
|
|
||||||
self._available = True
|
if not self._available:
|
||||||
|
_LOGGER.info(
|
||||||
|
"Restored connection to %s", self._config_entry.data[CONF_HOST]
|
||||||
|
)
|
||||||
|
self._available = True
|
||||||
|
|
||||||
if not is_on:
|
if not is_on:
|
||||||
self._state = STATE_OFF
|
self._state = STATE_OFF
|
||||||
@ -157,7 +155,7 @@ class VizioDevice(MediaPlayerDevice):
|
|||||||
async def _async_send_update_options_signal(
|
async def _async_send_update_options_signal(
|
||||||
hass: HomeAssistantType, config_entry: ConfigEntry
|
hass: HomeAssistantType, config_entry: ConfigEntry
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Send update event when when Vizio config entry is updated."""
|
"""Send update event when Vizio config entry is updated."""
|
||||||
# Move this method to component level if another entity ever gets added for a single config entry.
|
# Move this method to component level if another entity ever gets added for a single config entry.
|
||||||
# See here: https://github.com/home-assistant/home-assistant/pull/30653#discussion_r366426121
|
# See here: https://github.com/home-assistant/home-assistant/pull/30653#discussion_r366426121
|
||||||
async_dispatcher_send(hass, config_entry.entry_id, config_entry)
|
async_dispatcher_send(hass, config_entry.entry_id, config_entry)
|
||||||
@ -276,7 +274,7 @@ class VizioDevice(MediaPlayerDevice):
|
|||||||
await self._device.input_switch(source)
|
await self._device.input_switch(source)
|
||||||
|
|
||||||
async def async_volume_up(self) -> None:
|
async def async_volume_up(self) -> None:
|
||||||
"""Increasing volume of the device."""
|
"""Increase volume of the device."""
|
||||||
await self._device.vol_up(num=self._volume_step)
|
await self._device.vol_up(num=self._volume_step)
|
||||||
|
|
||||||
if self._volume_level is not None:
|
if self._volume_level is not None:
|
||||||
@ -285,7 +283,7 @@ class VizioDevice(MediaPlayerDevice):
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def async_volume_down(self) -> None:
|
async def async_volume_down(self) -> None:
|
||||||
"""Decreasing volume of the device."""
|
"""Decrease volume of the device."""
|
||||||
await self._device.vol_down(num=self._volume_step)
|
await self._device.vol_down(num=self._volume_step)
|
||||||
|
|
||||||
if self._volume_level is not None:
|
if self._volume_level is not None:
|
||||||
|
102
tests/components/vizio/conftest.py
Normal file
102
tests/components/vizio/conftest.py
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
"""Configure py.test."""
|
||||||
|
from asynctest import patch
|
||||||
|
import pytest
|
||||||
|
from pyvizio.const import DEVICE_CLASS_SPEAKER
|
||||||
|
from pyvizio.vizio import MAX_VOLUME
|
||||||
|
|
||||||
|
from .const import CURRENT_INPUT, INPUT_LIST, UNIQUE_ID
|
||||||
|
|
||||||
|
|
||||||
|
class MockInput:
|
||||||
|
"""Mock Vizio device input."""
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
"""Initialize mock Vizio device input."""
|
||||||
|
self.meta_name = name
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
|
||||||
|
def get_mock_inputs(input_list):
|
||||||
|
"""Return list of MockInput."""
|
||||||
|
return [MockInput(input) for input in input_list]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="skip_notifications", autouse=True)
|
||||||
|
def skip_notifications_fixture():
|
||||||
|
"""Skip notification calls."""
|
||||||
|
with patch("homeassistant.components.persistent_notification.async_create"), patch(
|
||||||
|
"homeassistant.components.persistent_notification.async_dismiss"
|
||||||
|
):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="vizio_connect")
|
||||||
|
def vizio_connect_fixture():
|
||||||
|
"""Mock valid vizio device and entry setup."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.vizio.config_flow.VizioAsync.validate_ha_config",
|
||||||
|
return_value=True,
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.vizio.config_flow.VizioAsync.get_unique_id",
|
||||||
|
return_value=UNIQUE_ID,
|
||||||
|
):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="vizio_bypass_setup")
|
||||||
|
def vizio_bypass_setup_fixture():
|
||||||
|
"""Mock component setup."""
|
||||||
|
with patch("homeassistant.components.vizio.async_setup_entry", return_value=True):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="vizio_bypass_update")
|
||||||
|
def vizio_bypass_update_fixture():
|
||||||
|
"""Mock component update."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.vizio.media_player.VizioAsync.can_connect",
|
||||||
|
return_value=True,
|
||||||
|
), patch("homeassistant.components.vizio.media_player.VizioDevice.async_update"):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="vizio_guess_device_type")
|
||||||
|
def vizio_guess_device_type_fixture():
|
||||||
|
"""Mock vizio async_guess_device_type function."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.vizio.config_flow.async_guess_device_type",
|
||||||
|
return_value="speaker",
|
||||||
|
):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="vizio_cant_connect")
|
||||||
|
def vizio_cant_connect_fixture():
|
||||||
|
"""Mock vizio device cant connect."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.vizio.config_flow.VizioAsync.validate_ha_config",
|
||||||
|
return_value=False,
|
||||||
|
):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="vizio_update")
|
||||||
|
def vizio_update_fixture():
|
||||||
|
"""Mock valid updates to vizio device."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.vizio.media_player.VizioAsync.can_connect",
|
||||||
|
return_value=True,
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.vizio.media_player.VizioAsync.get_current_volume",
|
||||||
|
return_value=int(MAX_VOLUME[DEVICE_CLASS_SPEAKER] / 2),
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.vizio.media_player.VizioAsync.get_current_input",
|
||||||
|
return_value=MockInput(CURRENT_INPUT),
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.vizio.media_player.VizioAsync.get_inputs",
|
||||||
|
return_value=get_mock_inputs(INPUT_LIST),
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.vizio.media_player.VizioAsync.get_power_state",
|
||||||
|
return_value=True,
|
||||||
|
):
|
||||||
|
yield
|
77
tests/components/vizio/const.py
Normal file
77
tests/components/vizio/const.py
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
"""Constants for the Vizio integration tests."""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from homeassistant.components.media_player import (
|
||||||
|
DEVICE_CLASS_SPEAKER,
|
||||||
|
DEVICE_CLASS_TV,
|
||||||
|
DOMAIN as MP_DOMAIN,
|
||||||
|
)
|
||||||
|
from homeassistant.components.vizio.const import CONF_VOLUME_STEP
|
||||||
|
from homeassistant.const import (
|
||||||
|
CONF_ACCESS_TOKEN,
|
||||||
|
CONF_DEVICE_CLASS,
|
||||||
|
CONF_HOST,
|
||||||
|
CONF_NAME,
|
||||||
|
CONF_PORT,
|
||||||
|
CONF_TYPE,
|
||||||
|
)
|
||||||
|
from homeassistant.util import slugify
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
NAME = "Vizio"
|
||||||
|
NAME2 = "Vizio2"
|
||||||
|
HOST = "192.168.1.1:9000"
|
||||||
|
HOST2 = "192.168.1.2:9000"
|
||||||
|
ACCESS_TOKEN = "deadbeef"
|
||||||
|
VOLUME_STEP = 2
|
||||||
|
UNIQUE_ID = "testid"
|
||||||
|
|
||||||
|
MOCK_USER_VALID_TV_CONFIG = {
|
||||||
|
CONF_NAME: NAME,
|
||||||
|
CONF_HOST: HOST,
|
||||||
|
CONF_DEVICE_CLASS: DEVICE_CLASS_TV,
|
||||||
|
CONF_ACCESS_TOKEN: ACCESS_TOKEN,
|
||||||
|
}
|
||||||
|
|
||||||
|
MOCK_OPTIONS = {
|
||||||
|
CONF_VOLUME_STEP: VOLUME_STEP,
|
||||||
|
}
|
||||||
|
|
||||||
|
MOCK_IMPORT_VALID_TV_CONFIG = {
|
||||||
|
CONF_NAME: NAME,
|
||||||
|
CONF_HOST: HOST,
|
||||||
|
CONF_DEVICE_CLASS: DEVICE_CLASS_TV,
|
||||||
|
CONF_ACCESS_TOKEN: ACCESS_TOKEN,
|
||||||
|
CONF_VOLUME_STEP: VOLUME_STEP,
|
||||||
|
}
|
||||||
|
|
||||||
|
MOCK_INVALID_TV_CONFIG = {
|
||||||
|
CONF_NAME: NAME,
|
||||||
|
CONF_HOST: HOST,
|
||||||
|
CONF_DEVICE_CLASS: DEVICE_CLASS_TV,
|
||||||
|
}
|
||||||
|
|
||||||
|
MOCK_SPEAKER_CONFIG = {
|
||||||
|
CONF_NAME: NAME,
|
||||||
|
CONF_HOST: HOST,
|
||||||
|
CONF_DEVICE_CLASS: DEVICE_CLASS_SPEAKER,
|
||||||
|
}
|
||||||
|
|
||||||
|
VIZIO_ZEROCONF_SERVICE_TYPE = "_viziocast._tcp.local."
|
||||||
|
ZEROCONF_NAME = f"{NAME}.{VIZIO_ZEROCONF_SERVICE_TYPE}"
|
||||||
|
ZEROCONF_HOST = HOST.split(":")[0]
|
||||||
|
ZEROCONF_PORT = HOST.split(":")[1]
|
||||||
|
|
||||||
|
MOCK_ZEROCONF_SERVICE_INFO = {
|
||||||
|
CONF_TYPE: VIZIO_ZEROCONF_SERVICE_TYPE,
|
||||||
|
CONF_NAME: ZEROCONF_NAME,
|
||||||
|
CONF_HOST: ZEROCONF_HOST,
|
||||||
|
CONF_PORT: ZEROCONF_PORT,
|
||||||
|
"properties": {"name": "SB4031-D5"},
|
||||||
|
}
|
||||||
|
|
||||||
|
CURRENT_INPUT = "HDMI"
|
||||||
|
INPUT_LIST = ["HDMI", "USB", "Bluetooth", "AUX"]
|
||||||
|
|
||||||
|
ENTITY_ID = f"{MP_DOMAIN}.{slugify(NAME)}"
|
@ -1,7 +1,4 @@
|
|||||||
"""Tests for Vizio config flow."""
|
"""Tests for Vizio config flow."""
|
||||||
import logging
|
|
||||||
|
|
||||||
from asynctest import patch
|
|
||||||
import pytest
|
import pytest
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
@ -20,113 +17,26 @@ from homeassistant.const import (
|
|||||||
CONF_DEVICE_CLASS,
|
CONF_DEVICE_CLASS,
|
||||||
CONF_HOST,
|
CONF_HOST,
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
CONF_PORT,
|
|
||||||
CONF_TYPE,
|
|
||||||
)
|
)
|
||||||
from homeassistant.helpers.typing import HomeAssistantType
|
from homeassistant.helpers.typing import HomeAssistantType
|
||||||
|
|
||||||
|
from .const import (
|
||||||
|
ACCESS_TOKEN,
|
||||||
|
HOST,
|
||||||
|
HOST2,
|
||||||
|
MOCK_IMPORT_VALID_TV_CONFIG,
|
||||||
|
MOCK_INVALID_TV_CONFIG,
|
||||||
|
MOCK_SPEAKER_CONFIG,
|
||||||
|
MOCK_USER_VALID_TV_CONFIG,
|
||||||
|
MOCK_ZEROCONF_SERVICE_INFO,
|
||||||
|
NAME,
|
||||||
|
NAME2,
|
||||||
|
UNIQUE_ID,
|
||||||
|
VOLUME_STEP,
|
||||||
|
)
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
NAME = "Vizio"
|
|
||||||
NAME2 = "Vizio2"
|
|
||||||
HOST = "192.168.1.1:9000"
|
|
||||||
HOST2 = "192.168.1.2:9000"
|
|
||||||
ACCESS_TOKEN = "deadbeef"
|
|
||||||
VOLUME_STEP = 2
|
|
||||||
UNIQUE_ID = "testid"
|
|
||||||
|
|
||||||
MOCK_USER_VALID_TV_CONFIG = {
|
|
||||||
CONF_NAME: NAME,
|
|
||||||
CONF_HOST: HOST,
|
|
||||||
CONF_DEVICE_CLASS: DEVICE_CLASS_TV,
|
|
||||||
CONF_ACCESS_TOKEN: ACCESS_TOKEN,
|
|
||||||
}
|
|
||||||
|
|
||||||
MOCK_IMPORT_VALID_TV_CONFIG = {
|
|
||||||
CONF_NAME: NAME,
|
|
||||||
CONF_HOST: HOST,
|
|
||||||
CONF_DEVICE_CLASS: DEVICE_CLASS_TV,
|
|
||||||
CONF_ACCESS_TOKEN: ACCESS_TOKEN,
|
|
||||||
CONF_VOLUME_STEP: VOLUME_STEP,
|
|
||||||
}
|
|
||||||
|
|
||||||
MOCK_INVALID_TV_CONFIG = {
|
|
||||||
CONF_NAME: NAME,
|
|
||||||
CONF_HOST: HOST,
|
|
||||||
CONF_DEVICE_CLASS: DEVICE_CLASS_TV,
|
|
||||||
}
|
|
||||||
|
|
||||||
MOCK_SPEAKER_CONFIG = {
|
|
||||||
CONF_NAME: NAME,
|
|
||||||
CONF_HOST: HOST,
|
|
||||||
CONF_DEVICE_CLASS: DEVICE_CLASS_SPEAKER,
|
|
||||||
}
|
|
||||||
|
|
||||||
VIZIO_ZEROCONF_SERVICE_TYPE = "_viziocast._tcp.local."
|
|
||||||
ZEROCONF_NAME = f"{NAME}.{VIZIO_ZEROCONF_SERVICE_TYPE}"
|
|
||||||
ZEROCONF_HOST = HOST.split(":")[0]
|
|
||||||
ZEROCONF_PORT = HOST.split(":")[1]
|
|
||||||
|
|
||||||
MOCK_ZEROCONF_ENTRY = {
|
|
||||||
CONF_TYPE: VIZIO_ZEROCONF_SERVICE_TYPE,
|
|
||||||
CONF_NAME: ZEROCONF_NAME,
|
|
||||||
CONF_HOST: ZEROCONF_HOST,
|
|
||||||
CONF_PORT: ZEROCONF_PORT,
|
|
||||||
"properties": {"name": "SB4031-D5"},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="vizio_connect")
|
|
||||||
def vizio_connect_fixture():
|
|
||||||
"""Mock valid vizio device and entry setup."""
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.vizio.config_flow.VizioAsync.validate_ha_config",
|
|
||||||
return_value=True,
|
|
||||||
), patch(
|
|
||||||
"homeassistant.components.vizio.config_flow.VizioAsync.get_unique_id",
|
|
||||||
return_value=UNIQUE_ID,
|
|
||||||
):
|
|
||||||
yield
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="vizio_bypass_setup")
|
|
||||||
def vizio_bypass_setup_fixture():
|
|
||||||
"""Mock component setup."""
|
|
||||||
with patch("homeassistant.components.vizio.async_setup_entry", return_value=True):
|
|
||||||
yield
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="vizio_bypass_update")
|
|
||||||
def vizio_bypass_update_fixture():
|
|
||||||
"""Mock component update."""
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.vizio.media_player.VizioAsync.can_connect",
|
|
||||||
return_value=True,
|
|
||||||
), patch("homeassistant.components.vizio.media_player.VizioDevice.async_update"):
|
|
||||||
yield
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="vizio_guess_device_type")
|
|
||||||
def vizio_guess_device_type_fixture():
|
|
||||||
"""Mock vizio async_guess_device_type function."""
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.vizio.config_flow.async_guess_device_type",
|
|
||||||
return_value="speaker",
|
|
||||||
):
|
|
||||||
yield
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="vizio_cant_connect")
|
|
||||||
def vizio_cant_connect_fixture():
|
|
||||||
"""Mock vizio device cant connect."""
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.vizio.config_flow.VizioAsync.validate_ha_config",
|
|
||||||
return_value=False,
|
|
||||||
):
|
|
||||||
yield
|
|
||||||
|
|
||||||
|
|
||||||
async def test_user_flow_minimum_fields(
|
async def test_user_flow_minimum_fields(
|
||||||
hass: HomeAssistantType,
|
hass: HomeAssistantType,
|
||||||
@ -142,12 +52,7 @@ async def test_user_flow_minimum_fields(
|
|||||||
assert result["step_id"] == "user"
|
assert result["step_id"] == "user"
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"], user_input=MOCK_SPEAKER_CONFIG
|
||||||
user_input={
|
|
||||||
CONF_NAME: NAME,
|
|
||||||
CONF_HOST: HOST,
|
|
||||||
CONF_DEVICE_CLASS: DEVICE_CLASS_SPEAKER,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
@ -172,13 +77,7 @@ async def test_user_flow_all_fields(
|
|||||||
assert result["step_id"] == "user"
|
assert result["step_id"] == "user"
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"], user_input=MOCK_USER_VALID_TV_CONFIG
|
||||||
user_input={
|
|
||||||
CONF_NAME: NAME,
|
|
||||||
CONF_HOST: HOST,
|
|
||||||
CONF_DEVICE_CLASS: DEVICE_CLASS_TV,
|
|
||||||
CONF_ACCESS_TOKEN: ACCESS_TOKEN,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
@ -408,7 +307,7 @@ async def test_zeroconf_flow(
|
|||||||
vizio_guess_device_type: pytest.fixture,
|
vizio_guess_device_type: pytest.fixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test zeroconf config flow."""
|
"""Test zeroconf config flow."""
|
||||||
discovery_info = MOCK_ZEROCONF_ENTRY.copy()
|
discovery_info = MOCK_ZEROCONF_SERVICE_INFO.copy()
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": SOURCE_ZEROCONF}, data=discovery_info
|
DOMAIN, context={"source": SOURCE_ZEROCONF}, data=discovery_info
|
||||||
)
|
)
|
||||||
@ -444,7 +343,7 @@ async def test_zeroconf_flow_already_configured(
|
|||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
# Try rediscovering same device
|
# Try rediscovering same device
|
||||||
discovery_info = MOCK_ZEROCONF_ENTRY.copy()
|
discovery_info = MOCK_ZEROCONF_SERVICE_INFO.copy()
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": SOURCE_ZEROCONF}, data=discovery_info
|
DOMAIN, context={"source": SOURCE_ZEROCONF}, data=discovery_info
|
||||||
)
|
)
|
||||||
|
43
tests/components/vizio/test_init.py
Normal file
43
tests/components/vizio/test_init.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
"""Tests for Vizio init."""
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components.media_player.const import DOMAIN as MP_DOMAIN
|
||||||
|
from homeassistant.components.vizio.const import DOMAIN
|
||||||
|
from homeassistant.helpers.typing import HomeAssistantType
|
||||||
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
|
from .const import MOCK_USER_VALID_TV_CONFIG, UNIQUE_ID
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
|
async def test_setup_component(
|
||||||
|
hass: HomeAssistantType,
|
||||||
|
vizio_connect: pytest.fixture,
|
||||||
|
vizio_update: pytest.fixture,
|
||||||
|
) -> None:
|
||||||
|
"""Test component setup."""
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass, DOMAIN, {DOMAIN: MOCK_USER_VALID_TV_CONFIG}
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert len(hass.states.async_entity_ids(MP_DOMAIN)) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_load_and_unload(
|
||||||
|
hass: HomeAssistantType,
|
||||||
|
vizio_connect: pytest.fixture,
|
||||||
|
vizio_update: pytest.fixture,
|
||||||
|
) -> None:
|
||||||
|
"""Test loading and unloading entry."""
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN, data=MOCK_USER_VALID_TV_CONFIG, unique_id=UNIQUE_ID
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert len(hass.states.async_entity_ids(MP_DOMAIN)) == 1
|
||||||
|
|
||||||
|
assert await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert len(hass.states.async_entity_ids(MP_DOMAIN)) == 0
|
297
tests/components/vizio/test_media_player.py
Normal file
297
tests/components/vizio/test_media_player.py
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
"""Tests for Vizio config flow."""
|
||||||
|
from datetime import timedelta
|
||||||
|
from unittest.mock import call
|
||||||
|
|
||||||
|
from asynctest import patch
|
||||||
|
import pytest
|
||||||
|
from pyvizio.const import (
|
||||||
|
DEVICE_CLASS_SPEAKER as VIZIO_DEVICE_CLASS_SPEAKER,
|
||||||
|
DEVICE_CLASS_TV as VIZIO_DEVICE_CLASS_TV,
|
||||||
|
)
|
||||||
|
from pyvizio.vizio import MAX_VOLUME
|
||||||
|
|
||||||
|
from homeassistant.components.media_player import (
|
||||||
|
ATTR_INPUT_SOURCE,
|
||||||
|
ATTR_MEDIA_VOLUME_LEVEL,
|
||||||
|
ATTR_MEDIA_VOLUME_MUTED,
|
||||||
|
DEVICE_CLASS_SPEAKER,
|
||||||
|
DEVICE_CLASS_TV,
|
||||||
|
DOMAIN as MP_DOMAIN,
|
||||||
|
SERVICE_MEDIA_NEXT_TRACK,
|
||||||
|
SERVICE_MEDIA_PREVIOUS_TRACK,
|
||||||
|
SERVICE_SELECT_SOURCE,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
SERVICE_VOLUME_DOWN,
|
||||||
|
SERVICE_VOLUME_MUTE,
|
||||||
|
SERVICE_VOLUME_SET,
|
||||||
|
SERVICE_VOLUME_UP,
|
||||||
|
)
|
||||||
|
from homeassistant.components.vizio.const import CONF_VOLUME_STEP, DOMAIN
|
||||||
|
from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON, STATE_UNAVAILABLE
|
||||||
|
from homeassistant.helpers.typing import HomeAssistantType
|
||||||
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
|
from .const import (
|
||||||
|
CURRENT_INPUT,
|
||||||
|
ENTITY_ID,
|
||||||
|
INPUT_LIST,
|
||||||
|
MOCK_SPEAKER_CONFIG,
|
||||||
|
MOCK_USER_VALID_TV_CONFIG,
|
||||||
|
NAME,
|
||||||
|
UNIQUE_ID,
|
||||||
|
VOLUME_STEP,
|
||||||
|
)
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||||
|
|
||||||
|
|
||||||
|
async def _test_setup(
|
||||||
|
hass: HomeAssistantType, ha_device_class: str, vizio_power_state: bool
|
||||||
|
) -> None:
|
||||||
|
"""Test Vizio Device entity setup."""
|
||||||
|
if vizio_power_state:
|
||||||
|
ha_power_state = STATE_ON
|
||||||
|
elif vizio_power_state is False:
|
||||||
|
ha_power_state = STATE_OFF
|
||||||
|
else:
|
||||||
|
ha_power_state = STATE_UNAVAILABLE
|
||||||
|
|
||||||
|
if ha_device_class == DEVICE_CLASS_SPEAKER:
|
||||||
|
vizio_device_class = VIZIO_DEVICE_CLASS_SPEAKER
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN, data=MOCK_SPEAKER_CONFIG, unique_id=UNIQUE_ID
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
vizio_device_class = VIZIO_DEVICE_CLASS_TV
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN, data=MOCK_USER_VALID_TV_CONFIG, unique_id=UNIQUE_ID
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.vizio.media_player.VizioAsync.get_current_volume",
|
||||||
|
return_value=int(MAX_VOLUME[vizio_device_class] / 2),
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.vizio.media_player.VizioAsync.get_power_state",
|
||||||
|
return_value=vizio_power_state,
|
||||||
|
):
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
attr = hass.states.get(ENTITY_ID).attributes
|
||||||
|
assert attr["friendly_name"] == NAME
|
||||||
|
assert attr["device_class"] == ha_device_class
|
||||||
|
|
||||||
|
assert hass.states.get(ENTITY_ID).state == ha_power_state
|
||||||
|
if ha_power_state == STATE_ON:
|
||||||
|
assert attr["source_list"] == INPUT_LIST
|
||||||
|
assert attr["source"] == CURRENT_INPUT
|
||||||
|
assert (
|
||||||
|
attr["volume_level"]
|
||||||
|
== float(int(MAX_VOLUME[vizio_device_class] / 2))
|
||||||
|
/ MAX_VOLUME[vizio_device_class]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def _test_setup_failure(hass: HomeAssistantType, config: str) -> None:
|
||||||
|
"""Test generic Vizio entity setup failure."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.vizio.media_player.VizioAsync.can_connect",
|
||||||
|
return_value=False,
|
||||||
|
):
|
||||||
|
config_entry = MockConfigEntry(domain=DOMAIN, data=config, unique_id=UNIQUE_ID)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert len(hass.states.async_entity_ids(MP_DOMAIN)) == 0
|
||||||
|
|
||||||
|
|
||||||
|
async def _test_service(
|
||||||
|
hass: HomeAssistantType,
|
||||||
|
vizio_func_name: str,
|
||||||
|
ha_service_name: str,
|
||||||
|
additional_service_data: dict = None,
|
||||||
|
*args,
|
||||||
|
**kwargs,
|
||||||
|
) -> None:
|
||||||
|
"""Test generic Vizio media player entity service."""
|
||||||
|
service_data = {ATTR_ENTITY_ID: ENTITY_ID}
|
||||||
|
if additional_service_data:
|
||||||
|
service_data.update(additional_service_data)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
f"homeassistant.components.vizio.media_player.VizioAsync.{vizio_func_name}"
|
||||||
|
) as service_call:
|
||||||
|
await hass.services.async_call(
|
||||||
|
MP_DOMAIN, ha_service_name, service_data=service_data, blocking=True,
|
||||||
|
)
|
||||||
|
assert service_call.called
|
||||||
|
|
||||||
|
if args or kwargs:
|
||||||
|
assert service_call.call_args == call(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_speaker_on(
|
||||||
|
hass: HomeAssistantType, vizio_connect: pytest.fixture, vizio_update: pytest.fixture
|
||||||
|
) -> None:
|
||||||
|
"""Test Vizio Speaker entity setup when on."""
|
||||||
|
await _test_setup(hass, DEVICE_CLASS_SPEAKER, True)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_speaker_off(
|
||||||
|
hass: HomeAssistantType, vizio_connect: pytest.fixture, vizio_update: pytest.fixture
|
||||||
|
) -> None:
|
||||||
|
"""Test Vizio Speaker entity setup when off."""
|
||||||
|
await _test_setup(hass, DEVICE_CLASS_SPEAKER, False)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_speaker_unavailable(
|
||||||
|
hass: HomeAssistantType, vizio_connect: pytest.fixture, vizio_update: pytest.fixture
|
||||||
|
) -> None:
|
||||||
|
"""Test Vizio Speaker entity setup when unavailable."""
|
||||||
|
await _test_setup(hass, DEVICE_CLASS_SPEAKER, None)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_init_tv_on(
|
||||||
|
hass: HomeAssistantType, vizio_connect: pytest.fixture, vizio_update: pytest.fixture
|
||||||
|
) -> None:
|
||||||
|
"""Test Vizio TV entity setup when on."""
|
||||||
|
await _test_setup(hass, DEVICE_CLASS_TV, True)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_init_tv_off(
|
||||||
|
hass: HomeAssistantType, vizio_connect: pytest.fixture, vizio_update: pytest.fixture
|
||||||
|
) -> None:
|
||||||
|
"""Test Vizio TV entity setup when off."""
|
||||||
|
await _test_setup(hass, DEVICE_CLASS_TV, False)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_init_tv_unavailable(
|
||||||
|
hass: HomeAssistantType, vizio_connect: pytest.fixture, vizio_update: pytest.fixture
|
||||||
|
) -> None:
|
||||||
|
"""Test Vizio TV entity setup when unavailable."""
|
||||||
|
await _test_setup(hass, DEVICE_CLASS_TV, None)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_setup_failure_speaker(
|
||||||
|
hass: HomeAssistantType, vizio_connect: pytest.fixture
|
||||||
|
) -> None:
|
||||||
|
"""Test speaker entity setup failure."""
|
||||||
|
await _test_setup_failure(hass, MOCK_SPEAKER_CONFIG)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_setup_failure_tv(
|
||||||
|
hass: HomeAssistantType, vizio_connect: pytest.fixture
|
||||||
|
) -> None:
|
||||||
|
"""Test TV entity setup failure."""
|
||||||
|
await _test_setup_failure(hass, MOCK_USER_VALID_TV_CONFIG)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_services(
|
||||||
|
hass: HomeAssistantType, vizio_connect: pytest.fixture, vizio_update: pytest.fixture
|
||||||
|
) -> None:
|
||||||
|
"""Test all Vizio media player entity services."""
|
||||||
|
await _test_setup(hass, DEVICE_CLASS_TV, True)
|
||||||
|
|
||||||
|
await _test_service(hass, "pow_on", SERVICE_TURN_ON)
|
||||||
|
await _test_service(hass, "pow_off", SERVICE_TURN_OFF)
|
||||||
|
await _test_service(
|
||||||
|
hass, "mute_on", SERVICE_VOLUME_MUTE, {ATTR_MEDIA_VOLUME_MUTED: True}
|
||||||
|
)
|
||||||
|
await _test_service(
|
||||||
|
hass, "mute_off", SERVICE_VOLUME_MUTE, {ATTR_MEDIA_VOLUME_MUTED: False}
|
||||||
|
)
|
||||||
|
await _test_service(
|
||||||
|
hass, "input_switch", SERVICE_SELECT_SOURCE, {ATTR_INPUT_SOURCE: "USB"}, "USB"
|
||||||
|
)
|
||||||
|
await _test_service(hass, "vol_up", SERVICE_VOLUME_UP)
|
||||||
|
await _test_service(hass, "vol_down", SERVICE_VOLUME_DOWN)
|
||||||
|
await _test_service(
|
||||||
|
hass, "vol_up", SERVICE_VOLUME_SET, {ATTR_MEDIA_VOLUME_LEVEL: 1}
|
||||||
|
)
|
||||||
|
await _test_service(
|
||||||
|
hass, "vol_down", SERVICE_VOLUME_SET, {ATTR_MEDIA_VOLUME_LEVEL: 0}
|
||||||
|
)
|
||||||
|
await _test_service(hass, "ch_up", SERVICE_MEDIA_NEXT_TRACK)
|
||||||
|
await _test_service(hass, "ch_down", SERVICE_MEDIA_PREVIOUS_TRACK)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_options_update(
|
||||||
|
hass: HomeAssistantType, vizio_connect: pytest.fixture, vizio_update: pytest.fixture
|
||||||
|
) -> None:
|
||||||
|
"""Test when config entry update event fires."""
|
||||||
|
await _test_setup(hass, DEVICE_CLASS_SPEAKER, True)
|
||||||
|
config_entry = hass.config_entries.async_entries(DOMAIN)[0]
|
||||||
|
assert config_entry.options
|
||||||
|
new_options = config_entry.options.copy()
|
||||||
|
updated_options = {CONF_VOLUME_STEP: VOLUME_STEP}
|
||||||
|
new_options.update(updated_options)
|
||||||
|
hass.config_entries.async_update_entry(
|
||||||
|
entry=config_entry, options=new_options,
|
||||||
|
)
|
||||||
|
assert config_entry.options == updated_options
|
||||||
|
await _test_service(hass, "vol_up", SERVICE_VOLUME_UP, num=VOLUME_STEP)
|
||||||
|
|
||||||
|
|
||||||
|
async def _test_update_availability_switch(
|
||||||
|
hass: HomeAssistantType,
|
||||||
|
initial_power_state: bool,
|
||||||
|
final_power_state: bool,
|
||||||
|
caplog: pytest.fixture,
|
||||||
|
) -> None:
|
||||||
|
now = dt_util.utcnow()
|
||||||
|
future_interval = timedelta(minutes=1)
|
||||||
|
|
||||||
|
# Setup device as if time is right now
|
||||||
|
with patch("homeassistant.util.dt.utcnow", return_value=now):
|
||||||
|
await _test_setup(hass, DEVICE_CLASS_SPEAKER, initial_power_state)
|
||||||
|
|
||||||
|
# Clear captured logs so that only availability state changes are captured for
|
||||||
|
# future assertion
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
# Fast forward time to future twice to trigger update and assert vizio log message
|
||||||
|
for i in range(1, 3):
|
||||||
|
future = now + (future_interval * i)
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.vizio.media_player.VizioAsync.get_power_state",
|
||||||
|
return_value=final_power_state,
|
||||||
|
), patch("homeassistant.util.dt.utcnow", return_value=future), patch(
|
||||||
|
"homeassistant.util.utcnow", return_value=future
|
||||||
|
):
|
||||||
|
async_fire_time_changed(hass, future)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
if final_power_state is None:
|
||||||
|
assert hass.states.get(ENTITY_ID).state == STATE_UNAVAILABLE
|
||||||
|
else:
|
||||||
|
assert hass.states.get(ENTITY_ID).state != STATE_UNAVAILABLE
|
||||||
|
|
||||||
|
# Ensure connection status messages from vizio.media_player appear exactly once
|
||||||
|
# (on availability state change)
|
||||||
|
vizio_log_list = [
|
||||||
|
log
|
||||||
|
for log in caplog.records
|
||||||
|
if log.name == "homeassistant.components.vizio.media_player"
|
||||||
|
]
|
||||||
|
assert len(vizio_log_list) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_update_unavailable_to_available(
|
||||||
|
hass: HomeAssistantType,
|
||||||
|
vizio_connect: pytest.fixture,
|
||||||
|
vizio_update: pytest.fixture,
|
||||||
|
caplog: pytest.fixture,
|
||||||
|
) -> None:
|
||||||
|
"""Test device becomes available after being unavailable."""
|
||||||
|
await _test_update_availability_switch(hass, None, True, caplog)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_update_available_to_unavailable(
|
||||||
|
hass: HomeAssistantType,
|
||||||
|
vizio_connect: pytest.fixture,
|
||||||
|
vizio_update: pytest.fixture,
|
||||||
|
caplog: pytest.fixture,
|
||||||
|
) -> None:
|
||||||
|
"""Test device becomes unavailable after being available."""
|
||||||
|
await _test_update_availability_switch(hass, True, None, caplog)
|
Loading…
x
Reference in New Issue
Block a user