Add support for Vizio sound mode (#33200)
* add sound mode support for devices that support it * make setting and unsetting flag better * move eq and audio settings into constants * fix missed statement to use constant instead of hardcoded string * further fixes based on review * bump pyvizio version to include newly identified app
This commit is contained in:
parent
f25321e010
commit
081b822d25
8 changed files with 79 additions and 11 deletions
|
@ -63,6 +63,9 @@ SUPPORTED_COMMANDS = {
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VIZIO_SOUND_MODE = "eq"
|
||||||
|
VIZIO_AUDIO_SETTINGS = "audio"
|
||||||
|
|
||||||
# Since Vizio component relies on device class, this dict will ensure that changes to
|
# Since Vizio component relies on device class, this dict will ensure that changes to
|
||||||
# the values of DEVICE_CLASS_SPEAKER or DEVICE_CLASS_TV don't require changes to pyvizio.
|
# the values of DEVICE_CLASS_SPEAKER or DEVICE_CLASS_TV don't require changes to pyvizio.
|
||||||
VIZIO_DEVICE_CLASSES = {
|
VIZIO_DEVICE_CLASSES = {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"domain": "vizio",
|
"domain": "vizio",
|
||||||
"name": "VIZIO SmartCast",
|
"name": "VIZIO SmartCast",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/vizio",
|
"documentation": "https://www.home-assistant.io/integrations/vizio",
|
||||||
"requirements": ["pyvizio==0.1.44"],
|
"requirements": ["pyvizio==0.1.45"],
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
"codeowners": ["@raman325"],
|
"codeowners": ["@raman325"],
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
|
|
|
@ -9,6 +9,7 @@ from pyvizio.const import APP_HOME, APPS, INPUT_APPS, NO_APP_RUNNING, UNKNOWN_AP
|
||||||
|
|
||||||
from homeassistant.components.media_player import (
|
from homeassistant.components.media_player import (
|
||||||
DEVICE_CLASS_SPEAKER,
|
DEVICE_CLASS_SPEAKER,
|
||||||
|
SUPPORT_SELECT_SOUND_MODE,
|
||||||
MediaPlayerDevice,
|
MediaPlayerDevice,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
@ -41,7 +42,9 @@ from .const import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
ICON,
|
ICON,
|
||||||
SUPPORTED_COMMANDS,
|
SUPPORTED_COMMANDS,
|
||||||
|
VIZIO_AUDIO_SETTINGS,
|
||||||
VIZIO_DEVICE_CLASSES,
|
VIZIO_DEVICE_CLASSES,
|
||||||
|
VIZIO_SOUND_MODE,
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -133,6 +136,8 @@ class VizioDevice(MediaPlayerDevice):
|
||||||
self._current_input = None
|
self._current_input = None
|
||||||
self._current_app = None
|
self._current_app = None
|
||||||
self._current_app_config = None
|
self._current_app_config = None
|
||||||
|
self._current_sound_mode = None
|
||||||
|
self._available_sound_modes = None
|
||||||
self._available_inputs = []
|
self._available_inputs = []
|
||||||
self._available_apps = []
|
self._available_apps = []
|
||||||
self._conf_apps = config_entry.options.get(CONF_APPS, {})
|
self._conf_apps = config_entry.options.get(CONF_APPS, {})
|
||||||
|
@ -191,17 +196,29 @@ class VizioDevice(MediaPlayerDevice):
|
||||||
self._current_app = None
|
self._current_app = None
|
||||||
self._current_app_config = None
|
self._current_app_config = None
|
||||||
self._available_apps = None
|
self._available_apps = None
|
||||||
|
self._current_sound_mode = None
|
||||||
|
self._available_sound_modes = None
|
||||||
return
|
return
|
||||||
|
|
||||||
self._state = STATE_ON
|
self._state = STATE_ON
|
||||||
|
|
||||||
audio_settings = await self._device.get_all_audio_settings(
|
audio_settings = await self._device.get_all_settings(
|
||||||
log_api_exception=False
|
VIZIO_AUDIO_SETTINGS, log_api_exception=False
|
||||||
)
|
)
|
||||||
if audio_settings is not None:
|
if audio_settings is not None:
|
||||||
self._volume_level = float(audio_settings["volume"]) / self._max_volume
|
self._volume_level = float(audio_settings["volume"]) / self._max_volume
|
||||||
self._is_muted = audio_settings["mute"].lower() == "on"
|
self._is_muted = audio_settings["mute"].lower() == "on"
|
||||||
|
|
||||||
|
if VIZIO_SOUND_MODE in audio_settings:
|
||||||
|
self._supported_commands |= SUPPORT_SELECT_SOUND_MODE
|
||||||
|
self._current_sound_mode = audio_settings[VIZIO_SOUND_MODE]
|
||||||
|
if self._available_sound_modes is None:
|
||||||
|
self._available_sound_modes = await self._device.get_setting_options(
|
||||||
|
VIZIO_AUDIO_SETTINGS, VIZIO_SOUND_MODE
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self._supported_commands ^= SUPPORT_SELECT_SOUND_MODE
|
||||||
|
|
||||||
input_ = await self._device.get_current_input(log_api_exception=False)
|
input_ = await self._device.get_current_input(log_api_exception=False)
|
||||||
if input_ is not None:
|
if input_ is not None:
|
||||||
self._current_input = input_
|
self._current_input = input_
|
||||||
|
@ -367,7 +384,7 @@ class VizioDevice(MediaPlayerDevice):
|
||||||
return self._config_entry.unique_id
|
return self._config_entry.unique_id
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_info(self):
|
def device_info(self) -> Dict[str, Any]:
|
||||||
"""Return device registry information."""
|
"""Return device registry information."""
|
||||||
return {
|
return {
|
||||||
"identifiers": {(DOMAIN, self._config_entry.unique_id)},
|
"identifiers": {(DOMAIN, self._config_entry.unique_id)},
|
||||||
|
@ -378,10 +395,27 @@ class VizioDevice(MediaPlayerDevice):
|
||||||
}
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_class(self):
|
def device_class(self) -> str:
|
||||||
"""Return device class for entity."""
|
"""Return device class for entity."""
|
||||||
return self._device_class
|
return self._device_class
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sound_mode(self) -> Optional[str]:
|
||||||
|
"""Name of the current sound mode."""
|
||||||
|
return self._current_sound_mode
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sound_mode_list(self) -> Optional[List[str]]:
|
||||||
|
"""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:
|
||||||
|
await self._device.set_setting(
|
||||||
|
VIZIO_AUDIO_SETTINGS, VIZIO_SOUND_MODE, sound_mode
|
||||||
|
)
|
||||||
|
|
||||||
async def async_turn_on(self) -> None:
|
async def async_turn_on(self) -> None:
|
||||||
"""Turn the device on."""
|
"""Turn the device on."""
|
||||||
await self._device.pow_on()
|
await self._device.pow_on()
|
||||||
|
|
|
@ -1738,7 +1738,7 @@ pyversasense==0.0.6
|
||||||
pyvesync==1.1.0
|
pyvesync==1.1.0
|
||||||
|
|
||||||
# homeassistant.components.vizio
|
# homeassistant.components.vizio
|
||||||
pyvizio==0.1.44
|
pyvizio==0.1.45
|
||||||
|
|
||||||
# homeassistant.components.velux
|
# homeassistant.components.velux
|
||||||
pyvlx==0.2.12
|
pyvlx==0.2.12
|
||||||
|
|
|
@ -650,7 +650,7 @@ pyvera==0.3.7
|
||||||
pyvesync==1.1.0
|
pyvesync==1.1.0
|
||||||
|
|
||||||
# homeassistant.components.vizio
|
# homeassistant.components.vizio
|
||||||
pyvizio==0.1.44
|
pyvizio==0.1.45
|
||||||
|
|
||||||
# homeassistant.components.html5
|
# homeassistant.components.html5
|
||||||
pywebpush==1.9.2
|
pywebpush==1.9.2
|
||||||
|
|
|
@ -8,7 +8,9 @@ from .const import (
|
||||||
APP_LIST,
|
APP_LIST,
|
||||||
CH_TYPE,
|
CH_TYPE,
|
||||||
CURRENT_APP_CONFIG,
|
CURRENT_APP_CONFIG,
|
||||||
|
CURRENT_EQ,
|
||||||
CURRENT_INPUT,
|
CURRENT_INPUT,
|
||||||
|
EQ_LIST,
|
||||||
INPUT_LIST,
|
INPUT_LIST,
|
||||||
INPUT_LIST_WITH_APPS,
|
INPUT_LIST_WITH_APPS,
|
||||||
MODEL,
|
MODEL,
|
||||||
|
@ -135,11 +137,15 @@ def vizio_update_fixture():
|
||||||
"homeassistant.components.vizio.media_player.VizioAsync.can_connect_with_auth_check",
|
"homeassistant.components.vizio.media_player.VizioAsync.can_connect_with_auth_check",
|
||||||
return_value=True,
|
return_value=True,
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.vizio.media_player.VizioAsync.get_all_audio_settings",
|
"homeassistant.components.vizio.media_player.VizioAsync.get_all_settings",
|
||||||
return_value={
|
return_value={
|
||||||
"volume": int(MAX_VOLUME[DEVICE_CLASS_SPEAKER] / 2),
|
"volume": int(MAX_VOLUME[DEVICE_CLASS_SPEAKER] / 2),
|
||||||
|
"eq": CURRENT_EQ,
|
||||||
"mute": "Off",
|
"mute": "Off",
|
||||||
},
|
},
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.vizio.media_player.VizioAsync.get_setting_options",
|
||||||
|
return_value=EQ_LIST,
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.vizio.media_player.VizioAsync.get_current_input",
|
"homeassistant.components.vizio.media_player.VizioAsync.get_current_input",
|
||||||
return_value=CURRENT_INPUT,
|
return_value=CURRENT_INPUT,
|
||||||
|
|
|
@ -64,6 +64,9 @@ class MockCompletePairingResponse(object):
|
||||||
self.auth_token = auth_token
|
self.auth_token = auth_token
|
||||||
|
|
||||||
|
|
||||||
|
CURRENT_EQ = "Music"
|
||||||
|
EQ_LIST = ["Music", "Movie"]
|
||||||
|
|
||||||
CURRENT_INPUT = "HDMI"
|
CURRENT_INPUT = "HDMI"
|
||||||
INPUT_LIST = ["HDMI", "USB", "Bluetooth", "AUX"]
|
INPUT_LIST = ["HDMI", "USB", "Bluetooth", "AUX"]
|
||||||
|
|
||||||
|
|
|
@ -21,11 +21,13 @@ from homeassistant.components.media_player import (
|
||||||
ATTR_INPUT_SOURCE,
|
ATTR_INPUT_SOURCE,
|
||||||
ATTR_MEDIA_VOLUME_LEVEL,
|
ATTR_MEDIA_VOLUME_LEVEL,
|
||||||
ATTR_MEDIA_VOLUME_MUTED,
|
ATTR_MEDIA_VOLUME_MUTED,
|
||||||
|
ATTR_SOUND_MODE,
|
||||||
DEVICE_CLASS_SPEAKER,
|
DEVICE_CLASS_SPEAKER,
|
||||||
DEVICE_CLASS_TV,
|
DEVICE_CLASS_TV,
|
||||||
DOMAIN as MP_DOMAIN,
|
DOMAIN as MP_DOMAIN,
|
||||||
SERVICE_MEDIA_NEXT_TRACK,
|
SERVICE_MEDIA_NEXT_TRACK,
|
||||||
SERVICE_MEDIA_PREVIOUS_TRACK,
|
SERVICE_MEDIA_PREVIOUS_TRACK,
|
||||||
|
SERVICE_SELECT_SOUND_MODE,
|
||||||
SERVICE_SELECT_SOURCE,
|
SERVICE_SELECT_SOURCE,
|
||||||
SERVICE_TURN_OFF,
|
SERVICE_TURN_OFF,
|
||||||
SERVICE_TURN_ON,
|
SERVICE_TURN_ON,
|
||||||
|
@ -58,9 +60,11 @@ from .const import (
|
||||||
APP_LIST,
|
APP_LIST,
|
||||||
CURRENT_APP,
|
CURRENT_APP,
|
||||||
CURRENT_APP_CONFIG,
|
CURRENT_APP_CONFIG,
|
||||||
|
CURRENT_EQ,
|
||||||
CURRENT_INPUT,
|
CURRENT_INPUT,
|
||||||
CUSTOM_CONFIG,
|
CUSTOM_CONFIG,
|
||||||
ENTITY_ID,
|
ENTITY_ID,
|
||||||
|
EQ_LIST,
|
||||||
INPUT_LIST,
|
INPUT_LIST,
|
||||||
INPUT_LIST_WITH_APPS,
|
INPUT_LIST_WITH_APPS,
|
||||||
MOCK_SPEAKER_APPS_FAILURE,
|
MOCK_SPEAKER_APPS_FAILURE,
|
||||||
|
@ -99,6 +103,11 @@ async def _test_setup(
|
||||||
data=vol.Schema(VIZIO_SCHEMA)(MOCK_SPEAKER_CONFIG),
|
data=vol.Schema(VIZIO_SCHEMA)(MOCK_SPEAKER_CONFIG),
|
||||||
unique_id=UNIQUE_ID,
|
unique_id=UNIQUE_ID,
|
||||||
)
|
)
|
||||||
|
dict_to_return = {
|
||||||
|
"volume": int(MAX_VOLUME[vizio_device_class] / 2),
|
||||||
|
"mute": "Off",
|
||||||
|
"eq": CURRENT_EQ,
|
||||||
|
}
|
||||||
else:
|
else:
|
||||||
vizio_device_class = VIZIO_DEVICE_CLASS_TV
|
vizio_device_class = VIZIO_DEVICE_CLASS_TV
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
|
@ -106,10 +115,17 @@ async def _test_setup(
|
||||||
data=vol.Schema(VIZIO_SCHEMA)(MOCK_USER_VALID_TV_CONFIG),
|
data=vol.Schema(VIZIO_SCHEMA)(MOCK_USER_VALID_TV_CONFIG),
|
||||||
unique_id=UNIQUE_ID,
|
unique_id=UNIQUE_ID,
|
||||||
)
|
)
|
||||||
|
dict_to_return = {
|
||||||
|
"volume": int(MAX_VOLUME[vizio_device_class] / 2),
|
||||||
|
"mute": "Off",
|
||||||
|
}
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.vizio.media_player.VizioAsync.get_all_audio_settings",
|
"homeassistant.components.vizio.media_player.VizioAsync.get_all_settings",
|
||||||
return_value={"volume": int(MAX_VOLUME[vizio_device_class] / 2), "mute": "Off"},
|
return_value=dict_to_return,
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.vizio.media_player.VizioAsync.get_setting_options",
|
||||||
|
return_value=EQ_LIST,
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.vizio.media_player.VizioAsync.get_power_state",
|
"homeassistant.components.vizio.media_player.VizioAsync.get_power_state",
|
||||||
return_value=vizio_power_state,
|
return_value=vizio_power_state,
|
||||||
|
@ -130,6 +146,9 @@ async def _test_setup(
|
||||||
assert attr["source"] == CURRENT_INPUT
|
assert attr["source"] == CURRENT_INPUT
|
||||||
if ha_device_class == DEVICE_CLASS_SPEAKER:
|
if ha_device_class == DEVICE_CLASS_SPEAKER:
|
||||||
assert not service_call.called
|
assert not service_call.called
|
||||||
|
assert "sound_mode" in attr
|
||||||
|
else:
|
||||||
|
assert "sound_mode" not in attr
|
||||||
assert (
|
assert (
|
||||||
attr["volume_level"]
|
attr["volume_level"]
|
||||||
== float(int(MAX_VOLUME[vizio_device_class] / 2))
|
== float(int(MAX_VOLUME[vizio_device_class] / 2))
|
||||||
|
@ -149,7 +168,7 @@ async def _test_setup_with_apps(
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.vizio.media_player.VizioAsync.get_all_audio_settings",
|
"homeassistant.components.vizio.media_player.VizioAsync.get_all_settings",
|
||||||
return_value={
|
return_value={
|
||||||
"volume": int(MAX_VOLUME[VIZIO_DEVICE_CLASS_TV] / 2),
|
"volume": int(MAX_VOLUME[VIZIO_DEVICE_CLASS_TV] / 2),
|
||||||
"mute": "Off",
|
"mute": "Off",
|
||||||
|
@ -351,6 +370,9 @@ async def test_services(
|
||||||
)
|
)
|
||||||
await _test_service(hass, "ch_up", SERVICE_MEDIA_NEXT_TRACK, None)
|
await _test_service(hass, "ch_up", SERVICE_MEDIA_NEXT_TRACK, None)
|
||||||
await _test_service(hass, "ch_down", SERVICE_MEDIA_PREVIOUS_TRACK, None)
|
await _test_service(hass, "ch_down", SERVICE_MEDIA_PREVIOUS_TRACK, None)
|
||||||
|
await _test_service(
|
||||||
|
hass, "set_setting", SERVICE_SELECT_SOUND_MODE, {ATTR_SOUND_MODE: "Music"}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_options_update(
|
async def test_options_update(
|
||||||
|
|
Loading…
Add table
Reference in a new issue