mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 05:07:41 +00:00
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
@ -27,6 +27,18 @@ from homeassistant.const import (
|
|||||||
)
|
)
|
||||||
import homeassistant.helpers.config_validation as cv
|
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_ADDITIONAL_CONFIGS = "additional_configs"
|
||||||
CONF_APP_ID = "APP_ID"
|
CONF_APP_ID = "APP_ID"
|
||||||
CONF_APPS = "apps"
|
CONF_APPS = "apps"
|
||||||
@ -66,6 +78,8 @@ SUPPORTED_COMMANDS = {
|
|||||||
VIZIO_SOUND_MODE = "eq"
|
VIZIO_SOUND_MODE = "eq"
|
||||||
VIZIO_AUDIO_SETTINGS = "audio"
|
VIZIO_AUDIO_SETTINGS = "audio"
|
||||||
VIZIO_MUTE_ON = "on"
|
VIZIO_MUTE_ON = "on"
|
||||||
|
VIZIO_VOLUME = "volume"
|
||||||
|
VIZIO_MUTE = "mute"
|
||||||
|
|
||||||
# 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.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""Vizio SmartCast Device support."""
|
"""Vizio SmartCast Device support."""
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
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 import VizioAsync
|
||||||
from pyvizio.api.apps import find_app_name
|
from pyvizio.api.apps import find_app_name
|
||||||
@ -24,6 +24,7 @@ from homeassistant.const import (
|
|||||||
STATE_ON,
|
STATE_ON,
|
||||||
)
|
)
|
||||||
from homeassistant.exceptions import PlatformNotReady
|
from homeassistant.exceptions import PlatformNotReady
|
||||||
|
from homeassistant.helpers import entity_platform
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
from homeassistant.helpers.dispatcher import (
|
from homeassistant.helpers.dispatcher import (
|
||||||
async_dispatcher_connect,
|
async_dispatcher_connect,
|
||||||
@ -41,16 +42,20 @@ from .const import (
|
|||||||
DEVICE_ID,
|
DEVICE_ID,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
ICON,
|
ICON,
|
||||||
|
SERVICE_UPDATE_SETTING,
|
||||||
SUPPORTED_COMMANDS,
|
SUPPORTED_COMMANDS,
|
||||||
|
UPDATE_SETTING_SCHEMA,
|
||||||
VIZIO_AUDIO_SETTINGS,
|
VIZIO_AUDIO_SETTINGS,
|
||||||
VIZIO_DEVICE_CLASSES,
|
VIZIO_DEVICE_CLASSES,
|
||||||
|
VIZIO_MUTE,
|
||||||
VIZIO_MUTE_ON,
|
VIZIO_MUTE_ON,
|
||||||
VIZIO_SOUND_MODE,
|
VIZIO_SOUND_MODE,
|
||||||
|
VIZIO_VOLUME,
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
SCAN_INTERVAL = timedelta(seconds=10)
|
SCAN_INTERVAL = timedelta(seconds=30)
|
||||||
PARALLEL_UPDATES = 0
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
|
|
||||||
@ -113,6 +118,10 @@ async def async_setup_entry(
|
|||||||
entity = VizioDevice(config_entry, device, name, device_class)
|
entity = VizioDevice(config_entry, device, name, device_class)
|
||||||
|
|
||||||
async_add_entities([entity], update_before_add=True)
|
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):
|
class VizioDevice(MediaPlayerEntity):
|
||||||
@ -203,10 +212,13 @@ class VizioDevice(MediaPlayerEntity):
|
|||||||
audio_settings = await self._device.get_all_settings(
|
audio_settings = await self._device.get_all_settings(
|
||||||
VIZIO_AUDIO_SETTINGS, log_api_exception=False
|
VIZIO_AUDIO_SETTINGS, log_api_exception=False
|
||||||
)
|
)
|
||||||
|
|
||||||
if audio_settings:
|
if audio_settings:
|
||||||
self._volume_level = float(audio_settings["volume"]) / self._max_volume
|
self._volume_level = float(audio_settings[VIZIO_VOLUME]) / self._max_volume
|
||||||
if "mute" in audio_settings:
|
if VIZIO_MUTE in audio_settings:
|
||||||
self._is_volume_muted = audio_settings["mute"].lower() == VIZIO_MUTE_ON
|
self._is_volume_muted = (
|
||||||
|
audio_settings[VIZIO_MUTE].lower() == VIZIO_MUTE_ON
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self._is_volume_muted = None
|
self._is_volume_muted = None
|
||||||
|
|
||||||
@ -274,6 +286,16 @@ class VizioDevice(MediaPlayerEntity):
|
|||||||
self._volume_step = config_entry.options[CONF_VOLUME_STEP]
|
self._volume_step = config_entry.options[CONF_VOLUME_STEP]
|
||||||
self._conf_apps.update(config_entry.options.get(CONF_APPS, {}))
|
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):
|
async def async_added_to_hass(self):
|
||||||
"""Register callbacks when entity is added."""
|
"""Register callbacks when entity is added."""
|
||||||
# Register callback for when config entry is updated.
|
# 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_APPS,
|
||||||
CONF_VOLUME_STEP,
|
CONF_VOLUME_STEP,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
SERVICE_UPDATE_SETTING,
|
||||||
VIZIO_SCHEMA,
|
VIZIO_SCHEMA,
|
||||||
)
|
)
|
||||||
from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON, STATE_UNAVAILABLE
|
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,
|
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(
|
async with _cm_for_test_setup_without_apps(
|
||||||
{
|
audio_settings, vizio_power_state,
|
||||||
"volume": int(MAX_VOLUME[VIZIO_DEVICE_CLASS_SPEAKER] / 2),
|
|
||||||
"mute": "Off",
|
|
||||||
"eq": CURRENT_EQ,
|
|
||||||
},
|
|
||||||
vizio_power_state,
|
|
||||||
):
|
):
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.vizio.media_player.VizioAsync.get_current_app_config",
|
"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(
|
async def _test_service(
|
||||||
hass: HomeAssistantType,
|
hass: HomeAssistantType,
|
||||||
|
domain: str,
|
||||||
vizio_func_name: str,
|
vizio_func_name: str,
|
||||||
ha_service_name: str,
|
ha_service_name: str,
|
||||||
additional_service_data: Optional[Dict[str, Any]],
|
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}"
|
f"homeassistant.components.vizio.media_player.VizioAsync.{vizio_func_name}"
|
||||||
) as service_call:
|
) as service_call:
|
||||||
await hass.services.async_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
|
assert service_call.called
|
||||||
|
|
||||||
@ -347,29 +350,49 @@ async def test_services(
|
|||||||
"""Test all Vizio media player entity services."""
|
"""Test all Vizio media player entity services."""
|
||||||
await _test_setup_tv(hass, True)
|
await _test_setup_tv(hass, True)
|
||||||
|
|
||||||
await _test_service(hass, "pow_on", SERVICE_TURN_ON, None)
|
await _test_service(hass, MP_DOMAIN, "pow_on", SERVICE_TURN_ON, None)
|
||||||
await _test_service(hass, "pow_off", SERVICE_TURN_OFF, None)
|
await _test_service(hass, MP_DOMAIN, "pow_off", SERVICE_TURN_OFF, None)
|
||||||
await _test_service(
|
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(
|
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(
|
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, MP_DOMAIN, "vol_up", SERVICE_VOLUME_UP, None)
|
||||||
await _test_service(hass, "vol_down", SERVICE_VOLUME_DOWN, None)
|
await _test_service(hass, MP_DOMAIN, "vol_down", SERVICE_VOLUME_DOWN, None)
|
||||||
await _test_service(
|
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(
|
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, MP_DOMAIN, "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_down", SERVICE_MEDIA_PREVIOUS_TRACK, None)
|
||||||
await _test_service(
|
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,
|
entry=config_entry, options=new_options,
|
||||||
)
|
)
|
||||||
assert config_entry.options == updated_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(
|
async def _test_update_availability_switch(
|
||||||
@ -474,6 +499,7 @@ async def test_setup_with_apps(
|
|||||||
|
|
||||||
await _test_service(
|
await _test_service(
|
||||||
hass,
|
hass,
|
||||||
|
MP_DOMAIN,
|
||||||
"launch_app",
|
"launch_app",
|
||||||
SERVICE_SELECT_SOURCE,
|
SERVICE_SELECT_SOURCE,
|
||||||
{ATTR_INPUT_SOURCE: CURRENT_APP},
|
{ATTR_INPUT_SOURCE: CURRENT_APP},
|
||||||
@ -550,6 +576,7 @@ async def test_setup_with_apps_additional_apps_config(
|
|||||||
|
|
||||||
await _test_service(
|
await _test_service(
|
||||||
hass,
|
hass,
|
||||||
|
MP_DOMAIN,
|
||||||
"launch_app",
|
"launch_app",
|
||||||
SERVICE_SELECT_SOURCE,
|
SERVICE_SELECT_SOURCE,
|
||||||
{ATTR_INPUT_SOURCE: "Netflix"},
|
{ATTR_INPUT_SOURCE: "Netflix"},
|
||||||
@ -557,6 +584,7 @@ async def test_setup_with_apps_additional_apps_config(
|
|||||||
)
|
)
|
||||||
await _test_service(
|
await _test_service(
|
||||||
hass,
|
hass,
|
||||||
|
MP_DOMAIN,
|
||||||
"launch_app_config",
|
"launch_app_config",
|
||||||
SERVICE_SELECT_SOURCE,
|
SERVICE_SELECT_SOURCE,
|
||||||
{ATTR_INPUT_SOURCE: CURRENT_APP},
|
{ATTR_INPUT_SOURCE: CURRENT_APP},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user