From d25aa1f1830f1d3fad42dde3f9bdfae2ad98b7f2 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Thu, 9 Jan 2020 21:53:47 -0500 Subject: [PATCH] Convert vizio component from sync to async component (#30605) * add device_info property and move component to async * use new VizioAsync class to have proper async support * remove hass from VizioDevice init since it is not needed * update requirements_all * missed type hint * updates based on review * pyvizio version bump * additional fixes based on review * mistake in last commit * remove device_info property because it can't be used unless this integration has config flow support --- homeassistant/components/vizio/__init__.py | 5 +- homeassistant/components/vizio/const.py | 1 - homeassistant/components/vizio/manifest.json | 2 +- .../components/vizio/media_player.py | 128 +++++++++++------- requirements_all.txt | 2 +- 5 files changed, 86 insertions(+), 52 deletions(-) diff --git a/homeassistant/components/vizio/__init__.py b/homeassistant/components/vizio/__init__.py index 3ffbf46f928..aa6f724bc59 100644 --- a/homeassistant/components/vizio/__init__.py +++ b/homeassistant/components/vizio/__init__.py @@ -1,4 +1,5 @@ """The vizio component.""" + import voluptuous as vol from homeassistant.const import ( @@ -8,6 +9,7 @@ from homeassistant.const import ( CONF_NAME, ) from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.typing import ConfigType from .const import ( CONF_VOLUME_STEP, @@ -17,7 +19,7 @@ from .const import ( ) -def validate_auth(config): +def validate_auth(config: ConfigType) -> ConfigType: """Validate presence of CONF_ACCESS_TOKEN when CONF_DEVICE_CLASS=tv.""" token = config.get(CONF_ACCESS_TOKEN) if config[CONF_DEVICE_CLASS] == "tv" and not token: @@ -25,6 +27,7 @@ def validate_auth(config): f"When '{CONF_DEVICE_CLASS}' is 'tv' then '{CONF_ACCESS_TOKEN}' is required.", path=[CONF_ACCESS_TOKEN], ) + return config diff --git a/homeassistant/components/vizio/const.py b/homeassistant/components/vizio/const.py index 828c4e600e0..c4c1ba3199b 100644 --- a/homeassistant/components/vizio/const.py +++ b/homeassistant/components/vizio/const.py @@ -6,7 +6,6 @@ DEFAULT_NAME = "Vizio SmartCast" DEFAULT_VOLUME_STEP = 1 DEFAULT_DEVICE_CLASS = "tv" DEVICE_ID = "pyvizio" -DEVICE_NAME = "Python Vizio" DOMAIN = "vizio" diff --git a/homeassistant/components/vizio/manifest.json b/homeassistant/components/vizio/manifest.json index 7ae0570fa86..5a4c0f4a4cc 100644 --- a/homeassistant/components/vizio/manifest.json +++ b/homeassistant/components/vizio/manifest.json @@ -2,7 +2,7 @@ "domain": "vizio", "name": "Vizio SmartCast TV", "documentation": "https://www.home-assistant.io/integrations/vizio", - "requirements": ["pyvizio==0.0.12"], + "requirements": ["pyvizio==0.0.15"], "dependencies": [], "codeowners": ["@raman325"] } diff --git a/homeassistant/components/vizio/media_player.py b/homeassistant/components/vizio/media_player.py index 418cf8e3835..84b745baf92 100644 --- a/homeassistant/components/vizio/media_player.py +++ b/homeassistant/components/vizio/media_player.py @@ -1,8 +1,10 @@ """Vizio SmartCast Device support.""" + from datetime import timedelta import logging +from typing import Any, Callable, Dict, List -from pyvizio import Vizio +from pyvizio import VizioAsync import voluptuous as vol from homeassistant import util @@ -25,9 +27,12 @@ from homeassistant.const import ( STATE_OFF, STATE_ON, ) +from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.helpers.entity import Entity +from homeassistant.helpers.typing import ConfigType, HomeAssistantType from . import VIZIO_SCHEMA, validate_auth -from .const import CONF_VOLUME_STEP, DEFAULT_NAME, DEVICE_ID, ICON +from .const import CONF_VOLUME_STEP, DEVICE_ID, ICON _LOGGER = logging.getLogger(__name__) @@ -52,32 +57,43 @@ SUPPORTED_COMMANDS = { PLATFORM_SCHEMA = vol.All(PLATFORM_SCHEMA.extend(VIZIO_SCHEMA), validate_auth) -def setup_platform(hass, config, add_entities, discovery_info=None): +async def async_setup_platform( + hass: HomeAssistantType, + config: ConfigType, + async_add_entities: Callable[[List[Entity], bool], None], + discovery_info: Dict[str, Any] = None, +): """Set up the Vizio media player platform.""" + host = config[CONF_HOST] token = config.get(CONF_ACCESS_TOKEN) name = config[CONF_NAME] volume_step = config[CONF_VOLUME_STEP] device_type = config[CONF_DEVICE_CLASS] - device = VizioDevice(host, token, name, volume_step, device_type) - if not device.validate_setup(): + + device = VizioAsync( + DEVICE_ID, host, name, token, device_type, async_get_clientsession(hass, False) + ) + if not await device.can_connect(): fail_auth_msg = "" if token: - fail_auth_msg = " and auth token is correct" + fail_auth_msg = ", auth token is correct" _LOGGER.error( "Failed to set up Vizio platform, please check if host " - "is valid and available%s", + "is valid and available, device type is correct%s", fail_auth_msg, ) return - add_entities([device], True) + async_add_entities([VizioDevice(device, name, volume_step, device_type)], True) class VizioDevice(MediaPlayerDevice): """Media Player implementation which performs REST requests to device.""" - def __init__(self, host, token, name, volume_step, device_type): + def __init__( + self, device: VizioAsync, name: str, volume_step: int, device_type: str + ) -> None: """Initialize Vizio device.""" self._name = name @@ -88,31 +104,32 @@ class VizioDevice(MediaPlayerDevice): self._available_inputs = None self._device_type = device_type self._supported_commands = SUPPORTED_COMMANDS[device_type] - self._device = Vizio(DEVICE_ID, host, DEFAULT_NAME, token, device_type) + self._device = device self._max_volume = float(self._device.get_max_volume()) self._unique_id = None self._icon = ICON[device_type] @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS) - def update(self): + async def async_update(self) -> None: """Retrieve latest state of the device.""" - is_on = self._device.get_power_state() if not self._unique_id: - self._unique_id = self._device.get_esn() + self._unique_id = await self._device.get_esn() + + is_on = await self._device.get_power_state() if is_on: self._state = STATE_ON - volume = self._device.get_current_volume() + volume = await self._device.get_current_volume() if volume is not None: self._volume_level = float(volume) / self._max_volume - input_ = self._device.get_current_input() + input_ = await self._device.get_current_input() if input_ is not None: self._current_input = input_.meta_name - inputs = self._device.get_inputs() + inputs = await self._device.get_inputs() if inputs is not None: self._available_inputs = [input_.name for input_ in inputs] @@ -127,100 +144,115 @@ class VizioDevice(MediaPlayerDevice): self._available_inputs = None @property - def state(self): + def state(self) -> str: """Return the state of the device.""" + return self._state @property - def name(self): + def name(self) -> str: """Return the name of the device.""" + return self._name @property - def icon(self): + def icon(self) -> str: """Return the icon of the device.""" + return self._icon @property - def volume_level(self): + def volume_level(self) -> float: """Return the volume level of the device.""" + return self._volume_level @property - def source(self): + def source(self) -> str: """Return current input of the device.""" + return self._current_input @property - def source_list(self): + def source_list(self) -> List: """Return list of available inputs of the device.""" + return self._available_inputs @property - def supported_features(self): + def supported_features(self) -> int: """Flag device features that are supported.""" + return self._supported_commands @property - def unique_id(self): + def unique_id(self) -> str: """Return the unique id of the device.""" + return self._unique_id - def turn_on(self): + async def async_turn_on(self) -> None: """Turn the device on.""" - self._device.pow_on() - def turn_off(self): + await self._device.pow_on() + + async def async_turn_off(self) -> None: """Turn the device off.""" - self._device.pow_off() - def mute_volume(self, mute): + await self._device.pow_off() + + async def async_mute_volume(self, mute: bool) -> None: """Mute the volume.""" + if mute: - self._device.mute_on() + await self._device.mute_on() else: - self._device.mute_off() + await self._device.mute_off() - def media_previous_track(self): + async def async_media_previous_track(self) -> None: """Send previous channel command.""" - self._device.ch_down() - def media_next_track(self): + await self._device.ch_down() + + async def async_media_next_track(self) -> None: """Send next channel command.""" - self._device.ch_up() - def select_source(self, source): + await self._device.ch_up() + + async def async_select_source(self, source: str) -> None: """Select input source.""" - self._device.input_switch(source) - def volume_up(self): + await self._device.input_switch(source) + + async def async_volume_up(self) -> None: """Increasing volume of the device.""" - self._device.vol_up(num=self._volume_step) + + await self._device.vol_up(self._volume_step) + if self._volume_level is not None: self._volume_level = min( 1.0, self._volume_level + self._volume_step / self._max_volume ) - def volume_down(self): + async def async_volume_down(self) -> None: """Decreasing volume of the device.""" - self._device.vol_down(num=self._volume_step) + + await self._device.vol_down(self._volume_step) + if self._volume_level is not None: self._volume_level = max( 0.0, self._volume_level - self._volume_step / self._max_volume ) - def validate_setup(self): - """Validate if host is available and auth token is correct.""" - return self._device.can_connect() - - def set_volume_level(self, 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)) + await self._device.vol_up(num) self._volume_level = volume - self._device.vol_up(num=num) elif volume < self._volume_level: num = int(self._max_volume * (self._volume_level - volume)) + await self._device.vol_down(num) self._volume_level = volume - self._device.vol_down(num=num) diff --git a/requirements_all.txt b/requirements_all.txt index 9fc7a4f27dc..0224969b479 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1696,7 +1696,7 @@ pyversasense==0.0.6 pyvesync==1.1.0 # homeassistant.components.vizio -pyvizio==0.0.12 +pyvizio==0.0.15 # homeassistant.components.velux pyvlx==0.2.12