mirror of
https://github.com/home-assistant/core
synced 2024-08-02 23:40:32 +02:00
Use entity attributes for vizio integration (#56093)
This commit is contained in:
parent
0dcd8b32ab
commit
f0e0b41f77
@ -33,7 +33,6 @@ from homeassistant.helpers.dispatcher import (
|
||||
async_dispatcher_connect,
|
||||
async_dispatcher_send,
|
||||
)
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
@ -142,16 +141,9 @@ class VizioDevice(MediaPlayerEntity):
|
||||
self._config_entry = config_entry
|
||||
self._apps_coordinator = apps_coordinator
|
||||
|
||||
self._name = name
|
||||
self._state = None
|
||||
self._volume_level = None
|
||||
self._volume_step = config_entry.options[CONF_VOLUME_STEP]
|
||||
self._is_volume_muted = None
|
||||
self._current_input = None
|
||||
self._current_app = None
|
||||
self._current_app_config = None
|
||||
self._current_sound_mode = None
|
||||
self._available_sound_modes = []
|
||||
self._available_inputs = []
|
||||
self._available_apps = []
|
||||
self._all_apps = apps_coordinator.data if apps_coordinator else None
|
||||
@ -159,14 +151,19 @@ class VizioDevice(MediaPlayerEntity):
|
||||
self._additional_app_configs = config_entry.data.get(CONF_APPS, {}).get(
|
||||
CONF_ADDITIONAL_CONFIGS, []
|
||||
)
|
||||
self._device_class = device_class
|
||||
self._supported_commands = SUPPORTED_COMMANDS[device_class]
|
||||
self._device = device
|
||||
self._max_volume = float(self._device.get_max_volume())
|
||||
self._icon = ICON[device_class]
|
||||
self._available = True
|
||||
self._model = None
|
||||
self._sw_version = None
|
||||
|
||||
# Entity class attributes that will change with each update (we only include
|
||||
# the ones that are initialized differently from the defaults)
|
||||
self._attr_sound_mode_list = []
|
||||
self._attr_supported_features = SUPPORTED_COMMANDS[device_class]
|
||||
|
||||
# Entity class attributes that will not change
|
||||
self._attr_name = name
|
||||
self._attr_icon = ICON[device_class]
|
||||
self._attr_unique_id = self._config_entry.unique_id
|
||||
self._attr_device_class = device_class
|
||||
|
||||
def _apps_list(self, apps: list[str]) -> list[str]:
|
||||
"""Return process apps list based on configured filters."""
|
||||
@ -183,64 +180,67 @@ class VizioDevice(MediaPlayerEntity):
|
||||
is_on = await self._device.get_power_state(log_api_exception=False)
|
||||
|
||||
if is_on is None:
|
||||
if self._available:
|
||||
if self._attr_available:
|
||||
_LOGGER.warning(
|
||||
"Lost connection to %s", self._config_entry.data[CONF_HOST]
|
||||
)
|
||||
self._available = False
|
||||
self._attr_available = False
|
||||
return
|
||||
|
||||
if not self._available:
|
||||
if not self._attr_available:
|
||||
_LOGGER.info(
|
||||
"Restored connection to %s", self._config_entry.data[CONF_HOST]
|
||||
)
|
||||
self._available = True
|
||||
self._attr_available = True
|
||||
|
||||
if not self._model:
|
||||
self._model = await self._device.get_model_name(log_api_exception=False)
|
||||
|
||||
if not self._sw_version:
|
||||
self._sw_version = await self._device.get_version(log_api_exception=False)
|
||||
if not self._attr_device_info:
|
||||
self._attr_device_info = {
|
||||
"identifiers": {(DOMAIN, self._attr_unique_id)},
|
||||
"name": self._attr_name,
|
||||
"manufacturer": "VIZIO",
|
||||
"model": await self._device.get_model_name(log_api_exception=False),
|
||||
"sw_version": await self._device.get_version(log_api_exception=False),
|
||||
}
|
||||
|
||||
if not is_on:
|
||||
self._state = STATE_OFF
|
||||
self._volume_level = None
|
||||
self._is_volume_muted = None
|
||||
self._attr_state = STATE_OFF
|
||||
self._attr_volume_level = None
|
||||
self._attr_is_volume_muted = None
|
||||
self._current_input = None
|
||||
self._current_app = None
|
||||
self._attr_app_name = None
|
||||
self._current_app_config = None
|
||||
self._current_sound_mode = None
|
||||
self._attr_sound_mode = None
|
||||
return
|
||||
|
||||
self._state = STATE_ON
|
||||
self._attr_state = STATE_ON
|
||||
|
||||
audio_settings = await self._device.get_all_settings(
|
||||
VIZIO_AUDIO_SETTINGS, log_api_exception=False
|
||||
)
|
||||
|
||||
if audio_settings:
|
||||
self._volume_level = float(audio_settings[VIZIO_VOLUME]) / self._max_volume
|
||||
self._attr_volume_level = (
|
||||
float(audio_settings[VIZIO_VOLUME]) / self._max_volume
|
||||
)
|
||||
if VIZIO_MUTE in audio_settings:
|
||||
self._is_volume_muted = (
|
||||
self._attr_is_volume_muted = (
|
||||
audio_settings[VIZIO_MUTE].lower() == VIZIO_MUTE_ON
|
||||
)
|
||||
else:
|
||||
self._is_volume_muted = None
|
||||
self._attr_is_volume_muted = None
|
||||
|
||||
if VIZIO_SOUND_MODE in audio_settings:
|
||||
self._supported_commands |= SUPPORT_SELECT_SOUND_MODE
|
||||
self._current_sound_mode = audio_settings[VIZIO_SOUND_MODE]
|
||||
if not self._available_sound_modes:
|
||||
self._available_sound_modes = (
|
||||
await self._device.get_setting_options(
|
||||
VIZIO_AUDIO_SETTINGS,
|
||||
VIZIO_SOUND_MODE,
|
||||
log_api_exception=False,
|
||||
)
|
||||
self._attr_supported_features |= SUPPORT_SELECT_SOUND_MODE
|
||||
self._attr_sound_mode = audio_settings[VIZIO_SOUND_MODE]
|
||||
if not self._attr_sound_mode_list:
|
||||
self._attr_sound_mode_list = await self._device.get_setting_options(
|
||||
VIZIO_AUDIO_SETTINGS,
|
||||
VIZIO_SOUND_MODE,
|
||||
log_api_exception=False,
|
||||
)
|
||||
else:
|
||||
# Explicitly remove SUPPORT_SELECT_SOUND_MODE from supported features
|
||||
self._supported_commands &= ~SUPPORT_SELECT_SOUND_MODE
|
||||
self._attr_supported_features &= ~SUPPORT_SELECT_SOUND_MODE
|
||||
|
||||
input_ = await self._device.get_current_input(log_api_exception=False)
|
||||
if input_:
|
||||
@ -255,7 +255,7 @@ class VizioDevice(MediaPlayerEntity):
|
||||
self._available_inputs = [input_.name for input_ in inputs]
|
||||
|
||||
# Return before setting app variables if INPUT_APPS isn't in available inputs
|
||||
if self._device_class == DEVICE_CLASS_SPEAKER or not any(
|
||||
if self._attr_device_class == DEVICE_CLASS_SPEAKER or not any(
|
||||
app for app in INPUT_APPS if app in self._available_inputs
|
||||
):
|
||||
return
|
||||
@ -268,13 +268,13 @@ class VizioDevice(MediaPlayerEntity):
|
||||
log_api_exception=False
|
||||
)
|
||||
|
||||
self._current_app = find_app_name(
|
||||
self._attr_app_name = find_app_name(
|
||||
self._current_app_config,
|
||||
[APP_HOME, *self._all_apps, *self._additional_app_configs],
|
||||
)
|
||||
|
||||
if self._current_app == NO_APP_RUNNING:
|
||||
self._current_app = None
|
||||
if self._attr_app_name == NO_APP_RUNNING:
|
||||
self._attr_app_name = None
|
||||
|
||||
def _get_additional_app_names(self) -> list[dict[str, Any]]:
|
||||
"""Return list of additional apps that were included in configuration.yaml."""
|
||||
@ -331,46 +331,16 @@ class VizioDevice(MediaPlayerEntity):
|
||||
self._all_apps = self._apps_coordinator.data
|
||||
self.async_write_ha_state()
|
||||
|
||||
if self._device_class == DEVICE_CLASS_TV:
|
||||
if self._attr_device_class == DEVICE_CLASS_TV:
|
||||
self.async_on_remove(
|
||||
self._apps_coordinator.async_add_listener(apps_list_update)
|
||||
)
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return the availabiliity of the device."""
|
||||
return self._available
|
||||
|
||||
@property
|
||||
def state(self) -> str | None:
|
||||
"""Return the state of the device."""
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return the name of the device."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def icon(self) -> str:
|
||||
"""Return the icon of the device."""
|
||||
return self._icon
|
||||
|
||||
@property
|
||||
def volume_level(self) -> float | None:
|
||||
"""Return the volume level of the device."""
|
||||
return self._volume_level
|
||||
|
||||
@property
|
||||
def is_volume_muted(self):
|
||||
"""Boolean if volume is currently muted."""
|
||||
return self._is_volume_muted
|
||||
|
||||
@property
|
||||
def source(self) -> str | None:
|
||||
"""Return current input of the device."""
|
||||
if self._current_app is not None and self._current_input in INPUT_APPS:
|
||||
return self._current_app
|
||||
if self._attr_app_name is not None and self._current_input in INPUT_APPS:
|
||||
return self._attr_app_name
|
||||
|
||||
return self._current_input
|
||||
|
||||
@ -378,7 +348,7 @@ class VizioDevice(MediaPlayerEntity):
|
||||
def source_list(self) -> list[str]:
|
||||
"""Return list of available inputs of the device."""
|
||||
# If Smartcast app is in input list, and the app list has been retrieved,
|
||||
# show the combination with , otherwise just return inputs
|
||||
# show the combination with, otherwise just return inputs
|
||||
if self._available_apps:
|
||||
return [
|
||||
*(
|
||||
@ -408,50 +378,9 @@ class VizioDevice(MediaPlayerEntity):
|
||||
|
||||
return None
|
||||
|
||||
@property
|
||||
def app_name(self) -> str | None:
|
||||
"""Return the friendly name of the current app."""
|
||||
return self._current_app
|
||||
|
||||
@property
|
||||
def supported_features(self) -> int:
|
||||
"""Flag device features that are supported."""
|
||||
return self._supported_commands
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
"""Return the unique id of the device."""
|
||||
return self._config_entry.unique_id
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return device registry information."""
|
||||
return {
|
||||
"identifiers": {(DOMAIN, self._config_entry.unique_id)},
|
||||
"name": self.name,
|
||||
"manufacturer": "VIZIO",
|
||||
"model": self._model,
|
||||
"sw_version": self._sw_version,
|
||||
}
|
||||
|
||||
@property
|
||||
def device_class(self) -> str:
|
||||
"""Return device class for entity."""
|
||||
return self._device_class
|
||||
|
||||
@property
|
||||
def sound_mode(self) -> str | None:
|
||||
"""Name of the current sound mode."""
|
||||
return self._current_sound_mode
|
||||
|
||||
@property
|
||||
def sound_mode_list(self) -> list[str] | None:
|
||||
"""List of available sound modes."""
|
||||
return self._available_sound_modes
|
||||
|
||||
async def async_select_sound_mode(self, sound_mode):
|
||||
"""Select sound mode."""
|
||||
if sound_mode in self._available_sound_modes:
|
||||
if sound_mode in self._attr_sound_mode_list:
|
||||
await self._device.set_setting(
|
||||
VIZIO_AUDIO_SETTINGS,
|
||||
VIZIO_SOUND_MODE,
|
||||
@ -471,10 +400,10 @@ class VizioDevice(MediaPlayerEntity):
|
||||
"""Mute the volume."""
|
||||
if mute:
|
||||
await self._device.mute_on(log_api_exception=False)
|
||||
self._is_volume_muted = True
|
||||
self._attr_is_volume_muted = True
|
||||
else:
|
||||
await self._device.mute_off(log_api_exception=False)
|
||||
self._is_volume_muted = False
|
||||
self._attr_is_volume_muted = False
|
||||
|
||||
async def async_media_previous_track(self) -> None:
|
||||
"""Send previous channel command."""
|
||||
@ -506,29 +435,29 @@ class VizioDevice(MediaPlayerEntity):
|
||||
"""Increase volume of the device."""
|
||||
await self._device.vol_up(num=self._volume_step, log_api_exception=False)
|
||||
|
||||
if self._volume_level is not None:
|
||||
self._volume_level = min(
|
||||
1.0, self._volume_level + self._volume_step / self._max_volume
|
||||
if self._attr_volume_level is not None:
|
||||
self._attr_volume_level = min(
|
||||
1.0, self._attr_volume_level + self._volume_step / self._max_volume
|
||||
)
|
||||
|
||||
async def async_volume_down(self) -> None:
|
||||
"""Decrease volume of the device."""
|
||||
await self._device.vol_down(num=self._volume_step, log_api_exception=False)
|
||||
|
||||
if self._volume_level is not None:
|
||||
self._volume_level = max(
|
||||
0.0, self._volume_level - self._volume_step / self._max_volume
|
||||
if self._attr_volume_level is not None:
|
||||
self._attr_volume_level = max(
|
||||
0.0, self._attr_volume_level - self._volume_step / self._max_volume
|
||||
)
|
||||
|
||||
async def async_set_volume_level(self, volume: float) -> None:
|
||||
"""Set volume level."""
|
||||
if self._volume_level is not None:
|
||||
if volume > self._volume_level:
|
||||
num = int(self._max_volume * (volume - self._volume_level))
|
||||
if self._attr_volume_level is not None:
|
||||
if volume > self._attr_volume_level:
|
||||
num = int(self._max_volume * (volume - self._attr_volume_level))
|
||||
await self._device.vol_up(num=num, log_api_exception=False)
|
||||
self._volume_level = volume
|
||||
self._attr_volume_level = volume
|
||||
|
||||
elif volume < self._volume_level:
|
||||
num = int(self._max_volume * (self._volume_level - volume))
|
||||
elif volume < self._attr_volume_level:
|
||||
num = int(self._max_volume * (self._attr_volume_level - volume))
|
||||
await self._device.vol_down(num=num, log_api_exception=False)
|
||||
self._volume_level = volume
|
||||
self._attr_volume_level = volume
|
||||
|
@ -38,6 +38,7 @@ from homeassistant.components.media_player import (
|
||||
SERVICE_VOLUME_SET,
|
||||
SERVICE_VOLUME_UP,
|
||||
)
|
||||
from homeassistant.components.media_player.const import ATTR_INPUT_SOURCE_LIST
|
||||
from homeassistant.components.vizio import validate_apps
|
||||
from homeassistant.components.vizio.const import (
|
||||
CONF_ADDITIONAL_CONFIGS,
|
||||
@ -102,8 +103,8 @@ def _get_ha_power_state(vizio_power_state: bool | None) -> str:
|
||||
|
||||
def _assert_sources_and_volume(attr: dict[str, Any], vizio_device_class: str) -> None:
|
||||
"""Assert source list, source, and volume level based on attr dict and device class."""
|
||||
assert attr["source_list"] == INPUT_LIST
|
||||
assert attr["source"] == CURRENT_INPUT
|
||||
assert attr[ATTR_INPUT_SOURCE_LIST] == INPUT_LIST
|
||||
assert attr[ATTR_INPUT_SOURCE] == CURRENT_INPUT
|
||||
assert (
|
||||
attr["volume_level"]
|
||||
== float(int(MAX_VOLUME[vizio_device_class] / 2))
|
||||
@ -236,7 +237,7 @@ def _assert_source_list_with_apps(
|
||||
if app_to_remove in list_to_test:
|
||||
list_to_test.remove(app_to_remove)
|
||||
|
||||
assert attr["source_list"] == list_to_test
|
||||
assert attr[ATTR_INPUT_SOURCE_LIST] == list_to_test
|
||||
|
||||
|
||||
async def _test_service(
|
||||
@ -533,8 +534,8 @@ async def test_setup_with_apps(
|
||||
):
|
||||
attr = hass.states.get(ENTITY_ID).attributes
|
||||
_assert_source_list_with_apps(list(INPUT_LIST_WITH_APPS + APP_NAME_LIST), attr)
|
||||
assert CURRENT_APP in attr["source_list"]
|
||||
assert attr["source"] == CURRENT_APP
|
||||
assert CURRENT_APP in attr[ATTR_INPUT_SOURCE_LIST]
|
||||
assert attr[ATTR_INPUT_SOURCE] == CURRENT_APP
|
||||
assert attr["app_name"] == CURRENT_APP
|
||||
assert "app_id" not in attr
|
||||
|
||||
@ -561,8 +562,8 @@ async def test_setup_with_apps_include(
|
||||
):
|
||||
attr = hass.states.get(ENTITY_ID).attributes
|
||||
_assert_source_list_with_apps(list(INPUT_LIST_WITH_APPS + [CURRENT_APP]), attr)
|
||||
assert CURRENT_APP in attr["source_list"]
|
||||
assert attr["source"] == CURRENT_APP
|
||||
assert CURRENT_APP in attr[ATTR_INPUT_SOURCE_LIST]
|
||||
assert attr[ATTR_INPUT_SOURCE] == CURRENT_APP
|
||||
assert attr["app_name"] == CURRENT_APP
|
||||
assert "app_id" not in attr
|
||||
|
||||
@ -579,8 +580,8 @@ async def test_setup_with_apps_exclude(
|
||||
):
|
||||
attr = hass.states.get(ENTITY_ID).attributes
|
||||
_assert_source_list_with_apps(list(INPUT_LIST_WITH_APPS + [CURRENT_APP]), attr)
|
||||
assert CURRENT_APP in attr["source_list"]
|
||||
assert attr["source"] == CURRENT_APP
|
||||
assert CURRENT_APP in attr[ATTR_INPUT_SOURCE_LIST]
|
||||
assert attr[ATTR_INPUT_SOURCE] == CURRENT_APP
|
||||
assert attr["app_name"] == CURRENT_APP
|
||||
assert "app_id" not in attr
|
||||
|
||||
@ -598,7 +599,7 @@ async def test_setup_with_apps_additional_apps_config(
|
||||
ADDITIONAL_APP_CONFIG["config"],
|
||||
):
|
||||
attr = hass.states.get(ENTITY_ID).attributes
|
||||
assert attr["source_list"].count(CURRENT_APP) == 1
|
||||
assert attr[ATTR_INPUT_SOURCE_LIST].count(CURRENT_APP) == 1
|
||||
_assert_source_list_with_apps(
|
||||
list(
|
||||
INPUT_LIST_WITH_APPS
|
||||
@ -613,8 +614,8 @@ async def test_setup_with_apps_additional_apps_config(
|
||||
),
|
||||
attr,
|
||||
)
|
||||
assert ADDITIONAL_APP_CONFIG["name"] in attr["source_list"]
|
||||
assert attr["source"] == ADDITIONAL_APP_CONFIG["name"]
|
||||
assert ADDITIONAL_APP_CONFIG["name"] in attr[ATTR_INPUT_SOURCE_LIST]
|
||||
assert attr[ATTR_INPUT_SOURCE] == ADDITIONAL_APP_CONFIG["name"]
|
||||
assert attr["app_name"] == ADDITIONAL_APP_CONFIG["name"]
|
||||
assert "app_id" not in attr
|
||||
|
||||
@ -673,7 +674,7 @@ async def test_setup_with_unknown_app_config(
|
||||
):
|
||||
attr = hass.states.get(ENTITY_ID).attributes
|
||||
_assert_source_list_with_apps(list(INPUT_LIST_WITH_APPS + APP_NAME_LIST), attr)
|
||||
assert attr["source"] == UNKNOWN_APP
|
||||
assert attr[ATTR_INPUT_SOURCE] == UNKNOWN_APP
|
||||
assert attr["app_name"] == UNKNOWN_APP
|
||||
assert attr["app_id"] == UNKNOWN_APP_CONFIG
|
||||
|
||||
@ -690,7 +691,7 @@ async def test_setup_with_no_running_app(
|
||||
):
|
||||
attr = hass.states.get(ENTITY_ID).attributes
|
||||
_assert_source_list_with_apps(list(INPUT_LIST_WITH_APPS + APP_NAME_LIST), attr)
|
||||
assert attr["source"] == "CAST"
|
||||
assert attr[ATTR_INPUT_SOURCE] == "CAST"
|
||||
assert "app_id" not in attr
|
||||
assert "app_name" not in attr
|
||||
|
||||
@ -735,7 +736,7 @@ async def test_apps_update(
|
||||
):
|
||||
# Check source list, remove TV inputs, and verify that the integration is
|
||||
# using the default APPS list
|
||||
sources = hass.states.get(ENTITY_ID).attributes["source_list"]
|
||||
sources = hass.states.get(ENTITY_ID).attributes[ATTR_INPUT_SOURCE_LIST]
|
||||
apps = list(set(sources) - set(INPUT_LIST))
|
||||
assert len(apps) == len(APPS)
|
||||
|
||||
@ -747,6 +748,6 @@ async def test_apps_update(
|
||||
await hass.async_block_till_done()
|
||||
# Check source list, remove TV inputs, and verify that the integration is
|
||||
# now using the APP_LIST list
|
||||
sources = hass.states.get(ENTITY_ID).attributes["source_list"]
|
||||
sources = hass.states.get(ENTITY_ID).attributes[ATTR_INPUT_SOURCE_LIST]
|
||||
apps = list(set(sources) - set(INPUT_LIST))
|
||||
assert len(apps) == len(APP_LIST)
|
||||
|
Loading…
Reference in New Issue
Block a user