Add vizio service to update a device setting (#36739)
* track all settings and add service to update a setting * sort setting types * reduce frequency of updates due to the increase in API calls per update * change dict call to a get in case audio settings aren't available unlikely to occur but less error prone * Update if statement to be more consistent * revert changes to track all settings and store in state machine * revert one more change * force setting_type and setting_name to lowercase to make it easier to understand how to make service call * make service calls even simpler by attempting to transform certain parameters as much as possible
This commit is contained in:
parent
937d993a67
commit
297dd9bbc2
4 changed files with 104 additions and 25 deletions
|
@ -27,6 +27,18 @@ from homeassistant.const import (
|
|||
)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
SERVICE_UPDATE_SETTING = "update_setting"
|
||||
|
||||
ATTR_SETTING_TYPE = "setting_type"
|
||||
ATTR_SETTING_NAME = "setting_name"
|
||||
ATTR_NEW_VALUE = "new_value"
|
||||
|
||||
UPDATE_SETTING_SCHEMA = {
|
||||
vol.Required(ATTR_SETTING_TYPE): cv.string,
|
||||
vol.Required(ATTR_SETTING_NAME): cv.string,
|
||||
vol.Required(ATTR_NEW_VALUE): vol.Or(vol.Coerce(int), cv.string),
|
||||
}
|
||||
|
||||
CONF_ADDITIONAL_CONFIGS = "additional_configs"
|
||||
CONF_APP_ID = "APP_ID"
|
||||
CONF_APPS = "apps"
|
||||
|
@ -66,6 +78,8 @@ SUPPORTED_COMMANDS = {
|
|||
VIZIO_SOUND_MODE = "eq"
|
||||
VIZIO_AUDIO_SETTINGS = "audio"
|
||||
VIZIO_MUTE_ON = "on"
|
||||
VIZIO_VOLUME = "volume"
|
||||
VIZIO_MUTE = "mute"
|
||||
|
||||
# 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.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"""Vizio SmartCast Device support."""
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import Any, Callable, Dict, List, Optional
|
||||
from typing import Any, Callable, Dict, List, Optional, Union
|
||||
|
||||
from pyvizio import VizioAsync
|
||||
from pyvizio.api.apps import find_app_name
|
||||
|
@ -24,6 +24,7 @@ from homeassistant.const import (
|
|||
STATE_ON,
|
||||
)
|
||||
from homeassistant.exceptions import PlatformNotReady
|
||||
from homeassistant.helpers import entity_platform
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.dispatcher import (
|
||||
async_dispatcher_connect,
|
||||
|
@ -41,16 +42,20 @@ from .const import (
|
|||
DEVICE_ID,
|
||||
DOMAIN,
|
||||
ICON,
|
||||
SERVICE_UPDATE_SETTING,
|
||||
SUPPORTED_COMMANDS,
|
||||
UPDATE_SETTING_SCHEMA,
|
||||
VIZIO_AUDIO_SETTINGS,
|
||||
VIZIO_DEVICE_CLASSES,
|
||||
VIZIO_MUTE,
|
||||
VIZIO_MUTE_ON,
|
||||
VIZIO_SOUND_MODE,
|
||||
VIZIO_VOLUME,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SCAN_INTERVAL = timedelta(seconds=10)
|
||||
SCAN_INTERVAL = timedelta(seconds=30)
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
|
||||
|
@ -113,6 +118,10 @@ async def async_setup_entry(
|
|||
entity = VizioDevice(config_entry, device, name, device_class)
|
||||
|
||||
async_add_entities([entity], update_before_add=True)
|
||||
platform = entity_platform.current_platform.get()
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_UPDATE_SETTING, UPDATE_SETTING_SCHEMA, "async_update_setting"
|
||||
)
|
||||
|
||||
|
||||
class VizioDevice(MediaPlayerEntity):
|
||||
|
@ -203,10 +212,13 @@ class VizioDevice(MediaPlayerEntity):
|
|||
audio_settings = await self._device.get_all_settings(
|
||||
VIZIO_AUDIO_SETTINGS, log_api_exception=False
|
||||
)
|
||||
|
||||
if audio_settings:
|
||||
self._volume_level = float(audio_settings["volume"]) / self._max_volume
|
||||
if "mute" in audio_settings:
|
||||
self._is_volume_muted = audio_settings["mute"].lower() == VIZIO_MUTE_ON
|
||||
self._volume_level = float(audio_settings[VIZIO_VOLUME]) / self._max_volume
|
||||
if VIZIO_MUTE in audio_settings:
|
||||
self._is_volume_muted = (
|
||||
audio_settings[VIZIO_MUTE].lower() == VIZIO_MUTE_ON
|
||||
)
|
||||
else:
|
||||
self._is_volume_muted = None
|
||||
|
||||
|
@ -274,6 +286,16 @@ class VizioDevice(MediaPlayerEntity):
|
|||
self._volume_step = config_entry.options[CONF_VOLUME_STEP]
|
||||
self._conf_apps.update(config_entry.options.get(CONF_APPS, {}))
|
||||
|
||||
async def async_update_setting(
|
||||
self, setting_type: str, setting_name: str, new_value: Union[int, str]
|
||||
) -> None:
|
||||
"""Update a setting when update_setting service is called."""
|
||||
await self._device.set_setting(
|
||||
setting_type.lower().replace(" ", "_"),
|
||||
setting_name.lower().replace(" ", "_"),
|
||||
new_value,
|
||||
)
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Register callbacks when entity is added."""
|
||||
# Register callback for when config entry is updated.
|
||||
|
|
15
homeassistant/components/vizio/services.yaml
Normal file
15
homeassistant/components/vizio/services.yaml
Normal file
|
@ -0,0 +1,15 @@
|
|||
update_setting:
|
||||
description: Update the value of a setting on a particular Vizio media player device.
|
||||
fields:
|
||||
entity_id:
|
||||
description: Name of an entity to send command.
|
||||
example: "media_player.vizio_smartcast"
|
||||
setting_type:
|
||||
description: The type of setting to be changed. Available types are listed in the `setting_types` property.
|
||||
example: "audio"
|
||||
setting_name:
|
||||
description: The name of the setting to be changed. Available settings for a given setting_type are listed in the `<setting_type>_settings` property.
|
||||
example: "eq"
|
||||
new_value:
|
||||
description: The new value for the setting
|
||||
example: "Music"
|
|
@ -41,6 +41,7 @@ from homeassistant.components.vizio.const import (
|
|||
CONF_APPS,
|
||||
CONF_VOLUME_STEP,
|
||||
DOMAIN,
|
||||
SERVICE_UPDATE_SETTING,
|
||||
VIZIO_SCHEMA,
|
||||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON, STATE_UNAVAILABLE
|
||||
|
@ -174,13 +175,14 @@ async def _test_setup_speaker(
|
|||
unique_id=UNIQUE_ID,
|
||||
)
|
||||
|
||||
audio_settings = {
|
||||
"volume": int(MAX_VOLUME[VIZIO_DEVICE_CLASS_SPEAKER] / 2),
|
||||
"mute": "Off",
|
||||
"eq": CURRENT_EQ,
|
||||
}
|
||||
|
||||
async with _cm_for_test_setup_without_apps(
|
||||
{
|
||||
"volume": int(MAX_VOLUME[VIZIO_DEVICE_CLASS_SPEAKER] / 2),
|
||||
"mute": "Off",
|
||||
"eq": CURRENT_EQ,
|
||||
},
|
||||
vizio_power_state,
|
||||
audio_settings, vizio_power_state,
|
||||
):
|
||||
with patch(
|
||||
"homeassistant.components.vizio.media_player.VizioAsync.get_current_app_config",
|
||||
|
@ -248,6 +250,7 @@ async def _test_setup_failure(hass: HomeAssistantType, config: str) -> None:
|
|||
|
||||
async def _test_service(
|
||||
hass: HomeAssistantType,
|
||||
domain: str,
|
||||
vizio_func_name: str,
|
||||
ha_service_name: str,
|
||||
additional_service_data: Optional[Dict[str, Any]],
|
||||
|
@ -263,7 +266,7 @@ async def _test_service(
|
|||
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,
|
||||
domain, ha_service_name, service_data=service_data, blocking=True,
|
||||
)
|
||||
assert service_call.called
|
||||
|
||||
|
@ -347,29 +350,49 @@ async def test_services(
|
|||
"""Test all Vizio media player entity services."""
|
||||
await _test_setup_tv(hass, True)
|
||||
|
||||
await _test_service(hass, "pow_on", SERVICE_TURN_ON, None)
|
||||
await _test_service(hass, "pow_off", SERVICE_TURN_OFF, None)
|
||||
await _test_service(hass, MP_DOMAIN, "pow_on", SERVICE_TURN_ON, None)
|
||||
await _test_service(hass, MP_DOMAIN, "pow_off", SERVICE_TURN_OFF, None)
|
||||
await _test_service(
|
||||
hass, "mute_on", SERVICE_VOLUME_MUTE, {ATTR_MEDIA_VOLUME_MUTED: True}
|
||||
hass, MP_DOMAIN, "mute_on", SERVICE_VOLUME_MUTE, {ATTR_MEDIA_VOLUME_MUTED: True}
|
||||
)
|
||||
await _test_service(
|
||||
hass, "mute_off", SERVICE_VOLUME_MUTE, {ATTR_MEDIA_VOLUME_MUTED: False}
|
||||
hass,
|
||||
MP_DOMAIN,
|
||||
"mute_off",
|
||||
SERVICE_VOLUME_MUTE,
|
||||
{ATTR_MEDIA_VOLUME_MUTED: False},
|
||||
)
|
||||
await _test_service(
|
||||
hass, "set_input", SERVICE_SELECT_SOURCE, {ATTR_INPUT_SOURCE: "USB"}, "USB"
|
||||
hass,
|
||||
MP_DOMAIN,
|
||||
"set_input",
|
||||
SERVICE_SELECT_SOURCE,
|
||||
{ATTR_INPUT_SOURCE: "USB"},
|
||||
"USB",
|
||||
)
|
||||
await _test_service(hass, "vol_up", SERVICE_VOLUME_UP, None)
|
||||
await _test_service(hass, "vol_down", SERVICE_VOLUME_DOWN, None)
|
||||
await _test_service(hass, MP_DOMAIN, "vol_up", SERVICE_VOLUME_UP, None)
|
||||
await _test_service(hass, MP_DOMAIN, "vol_down", SERVICE_VOLUME_DOWN, None)
|
||||
await _test_service(
|
||||
hass, "vol_up", SERVICE_VOLUME_SET, {ATTR_MEDIA_VOLUME_LEVEL: 1}
|
||||
hass, MP_DOMAIN, "vol_up", SERVICE_VOLUME_SET, {ATTR_MEDIA_VOLUME_LEVEL: 1}
|
||||
)
|
||||
await _test_service(
|
||||
hass, "vol_down", SERVICE_VOLUME_SET, {ATTR_MEDIA_VOLUME_LEVEL: 0}
|
||||
hass, MP_DOMAIN, "vol_down", SERVICE_VOLUME_SET, {ATTR_MEDIA_VOLUME_LEVEL: 0}
|
||||
)
|
||||
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, MP_DOMAIN, "ch_up", SERVICE_MEDIA_NEXT_TRACK, None)
|
||||
await _test_service(hass, MP_DOMAIN, "ch_down", SERVICE_MEDIA_PREVIOUS_TRACK, None)
|
||||
await _test_service(
|
||||
hass, "set_setting", SERVICE_SELECT_SOUND_MODE, {ATTR_SOUND_MODE: "Music"}
|
||||
hass,
|
||||
MP_DOMAIN,
|
||||
"set_setting",
|
||||
SERVICE_SELECT_SOUND_MODE,
|
||||
{ATTR_SOUND_MODE: "Music"},
|
||||
)
|
||||
await _test_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
"set_setting",
|
||||
SERVICE_UPDATE_SETTING,
|
||||
{"setting_type": "Audio", "setting_name": "EQ", "new_value": "Music"},
|
||||
)
|
||||
|
||||
|
||||
|
@ -389,7 +412,9 @@ async def test_options_update(
|
|||
entry=config_entry, options=new_options,
|
||||
)
|
||||
assert config_entry.options == updated_options
|
||||
await _test_service(hass, "vol_up", SERVICE_VOLUME_UP, None, num=VOLUME_STEP)
|
||||
await _test_service(
|
||||
hass, MP_DOMAIN, "vol_up", SERVICE_VOLUME_UP, None, num=VOLUME_STEP
|
||||
)
|
||||
|
||||
|
||||
async def _test_update_availability_switch(
|
||||
|
@ -474,6 +499,7 @@ async def test_setup_with_apps(
|
|||
|
||||
await _test_service(
|
||||
hass,
|
||||
MP_DOMAIN,
|
||||
"launch_app",
|
||||
SERVICE_SELECT_SOURCE,
|
||||
{ATTR_INPUT_SOURCE: CURRENT_APP},
|
||||
|
@ -550,6 +576,7 @@ async def test_setup_with_apps_additional_apps_config(
|
|||
|
||||
await _test_service(
|
||||
hass,
|
||||
MP_DOMAIN,
|
||||
"launch_app",
|
||||
SERVICE_SELECT_SOURCE,
|
||||
{ATTR_INPUT_SOURCE: "Netflix"},
|
||||
|
@ -557,6 +584,7 @@ async def test_setup_with_apps_additional_apps_config(
|
|||
)
|
||||
await _test_service(
|
||||
hass,
|
||||
MP_DOMAIN,
|
||||
"launch_app_config",
|
||||
SERVICE_SELECT_SOURCE,
|
||||
{ATTR_INPUT_SOURCE: CURRENT_APP},
|
||||
|
|
Loading…
Add table
Reference in a new issue