Update vizio component to support latest pyvizio with soundbar support (#22294)

* update vizio component to support latest pyvizio with soundbar support

* Resolved Hound issues

* Additional Hound issue

* Updated based on feedback

* Style updates

* Additional code styling changes

* Added check for auth token not being set for tv device_class

* Limited lines to 80 characters

* moved MAX_VOLUME into base package

* fixed supported commands

* styling changes

* fix styling yet again

* remove unnecessary elif

* removed play/pause since I can't get current state

* changed value access method from config dict

* fixed flake failures

* try to fix docstring

* try to fix docstring

* fixed auth token validation

* rebase and regenerate requirements_all.txt

* updated log text

* line length fix

* added config validation to handle conditionally optional parameter

* updated validate setup log message and string formatting based on review

* fix pylint error

* less ugly
This commit is contained in:
Raman Gupta 2019-04-18 16:48:05 -04:00 committed by Martin Hjelmare
parent e1d1f21a74
commit 620c6a22ac
4 changed files with 108 additions and 54 deletions

View File

@ -237,6 +237,7 @@ homeassistant/components/uptimerobot/* @ludeeus
homeassistant/components/utility_meter/* @dgomes homeassistant/components/utility_meter/* @dgomes
homeassistant/components/velux/* @Julius2342 homeassistant/components/velux/* @Julius2342
homeassistant/components/version/* @fabaff homeassistant/components/version/* @fabaff
homeassistant/components/vizio/* @raman325
homeassistant/components/waqi/* @andrey-git homeassistant/components/waqi/* @andrey-git
homeassistant/components/weather/* @fabaff homeassistant/components/weather/* @fabaff
homeassistant/components/weblink/* @home-assistant/core homeassistant/components/weblink/* @home-assistant/core

View File

@ -3,8 +3,8 @@
"name": "Vizio", "name": "Vizio",
"documentation": "https://www.home-assistant.io/components/vizio", "documentation": "https://www.home-assistant.io/components/vizio",
"requirements": [ "requirements": [
"pyvizio==0.0.4" "pyvizio==0.0.7"
], ],
"dependencies": [], "dependencies": [],
"codeowners": [] "codeowners": ["@raman325"]
} }

View File

@ -1,18 +1,30 @@
"""Vizio SmartCast TV support.""" """Vizio SmartCast Device support."""
from datetime import timedelta from datetime import timedelta
import logging import logging
import voluptuous as vol import voluptuous as vol
from homeassistant import util from homeassistant import util
from homeassistant.components.media_player import ( from homeassistant.components.media_player import (
MediaPlayerDevice, PLATFORM_SCHEMA) MediaPlayerDevice,
PLATFORM_SCHEMA
)
from homeassistant.components.media_player.const import ( from homeassistant.components.media_player.const import (
SUPPORT_NEXT_TRACK, SUPPORT_PREVIOUS_TRACK, SUPPORT_NEXT_TRACK,
SUPPORT_SELECT_SOURCE, SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_PREVIOUS_TRACK,
SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_STEP) SUPPORT_SELECT_SOURCE,
SUPPORT_TURN_OFF,
SUPPORT_TURN_ON,
SUPPORT_VOLUME_MUTE,
SUPPORT_VOLUME_SET,
SUPPORT_VOLUME_STEP
)
from homeassistant.const import ( from homeassistant.const import (
CONF_ACCESS_TOKEN, CONF_HOST, CONF_NAME, STATE_OFF, STATE_ON) CONF_ACCESS_TOKEN,
CONF_DEVICE_CLASS,
CONF_HOST,
CONF_NAME,
STATE_OFF,
STATE_ON
)
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -22,6 +34,7 @@ CONF_VOLUME_STEP = 'volume_step'
DEFAULT_NAME = 'Vizio SmartCast' DEFAULT_NAME = 'Vizio SmartCast'
DEFAULT_VOLUME_STEP = 1 DEFAULT_VOLUME_STEP = 1
DEFAULT_DEVICE_CLASS = 'tv'
DEVICE_ID = 'pyvizio' DEVICE_ID = 'pyvizio'
DEVICE_NAME = 'Python Vizio' DEVICE_NAME = 'Python Vizio'
@ -30,36 +43,71 @@ ICON = 'mdi:television'
MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1) MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1)
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
SUPPORTED_COMMANDS = SUPPORT_TURN_ON | SUPPORT_TURN_OFF \ COMMON_SUPPORTED_COMMANDS = (
| SUPPORT_SELECT_SOURCE \ SUPPORT_SELECT_SOURCE |
| SUPPORT_NEXT_TRACK | SUPPORT_PREVIOUS_TRACK \ SUPPORT_TURN_ON |
| SUPPORT_VOLUME_MUTE | SUPPORT_VOLUME_STEP \ SUPPORT_TURN_OFF |
| SUPPORT_VOLUME_SET SUPPORT_VOLUME_MUTE |
SUPPORT_VOLUME_SET |
SUPPORT_VOLUME_STEP
)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ SUPPORTED_COMMANDS = {
'soundbar': COMMON_SUPPORTED_COMMANDS,
'tv': (
COMMON_SUPPORTED_COMMANDS |
SUPPORT_NEXT_TRACK |
SUPPORT_PREVIOUS_TRACK
)
}
def validate_auth(config):
"""Validate presence of CONF_ACCESS_TOKEN when CONF_DEVICE_CLASS=tv."""
token = config.get(CONF_ACCESS_TOKEN)
if config[CONF_DEVICE_CLASS] == 'tv' and (token is None or token == ''):
raise vol.Invalid(
"When '{}' is 'tv' then '{}' is required.".format(
CONF_DEVICE_CLASS,
CONF_ACCESS_TOKEN,
),
path=[CONF_ACCESS_TOKEN],
)
return config
PLATFORM_SCHEMA = vol.All(
PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string, vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_ACCESS_TOKEN): cv.string, vol.Optional(CONF_ACCESS_TOKEN): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_SUPPRESS_WARNING, default=False): cv.boolean, vol.Optional(CONF_SUPPRESS_WARNING, default=False): cv.boolean,
vol.Optional(CONF_DEVICE_CLASS, default=DEFAULT_DEVICE_CLASS):
vol.All(cv.string, vol.Lower, vol.In(['tv', 'soundbar'])),
vol.Optional(CONF_VOLUME_STEP, default=DEFAULT_VOLUME_STEP): vol.Optional(CONF_VOLUME_STEP, default=DEFAULT_VOLUME_STEP):
vol.All(vol.Coerce(int), vol.Range(min=1, max=10)), vol.All(vol.Coerce(int), vol.Range(min=1, max=10)),
}) }),
validate_auth,
)
def setup_platform(hass, config, add_entities, discovery_info=None): def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the VizioTV media player platform.""" """Set up the Vizio media player platform."""
host = config.get(CONF_HOST) host = config[CONF_HOST]
token = config.get(CONF_ACCESS_TOKEN) token = config.get(CONF_ACCESS_TOKEN)
name = config.get(CONF_NAME) name = config[CONF_NAME]
volume_step = config.get(CONF_VOLUME_STEP) volume_step = config[CONF_VOLUME_STEP]
device_type = config[CONF_DEVICE_CLASS]
device = VizioDevice(host, token, name, volume_step) device = VizioDevice(host, token, name, volume_step, device_type)
if device.validate_setup() is False: if device.validate_setup() is False:
_LOGGER.error("Failed to set up Vizio TV platform, " fail_auth_msg = ""
"please check if host and API key are correct") if token is not None and token != '':
fail_auth_msg = " and auth token is correct"
_LOGGER.error("Failed to set up Vizio platform, please check if host "
"is valid and available%s", fail_auth_msg)
return return
if config.get(CONF_SUPPRESS_WARNING): if config[CONF_SUPPRESS_WARNING]:
from requests.packages import urllib3 from requests.packages import urllib3
_LOGGER.warning("InsecureRequestWarning is disabled " _LOGGER.warning("InsecureRequestWarning is disabled "
"because of Vizio platform configuration") "because of Vizio platform configuration")
@ -68,22 +116,27 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
class VizioDevice(MediaPlayerDevice): class VizioDevice(MediaPlayerDevice):
"""Media Player implementation which performs REST requests to TV.""" """Media Player implementation which performs REST requests to device."""
def __init__(self, host, token, name, volume_step): def __init__(self, host, token, name, volume_step, device_type):
"""Initialize Vizio device.""" """Initialize Vizio device."""
import pyvizio import pyvizio
self._device = pyvizio.Vizio(DEVICE_ID, host, DEFAULT_NAME, token)
self._name = name self._name = name
self._state = None self._state = None
self._volume_level = None self._volume_level = None
self._volume_step = volume_step self._volume_step = volume_step
self._current_input = None self._current_input = None
self._available_inputs = None self._available_inputs = None
self._device_type = device_type
self._supported_commands = SUPPORTED_COMMANDS[device_type]
self._device = pyvizio.Vizio(DEVICE_ID, host, DEFAULT_NAME, token,
device_type)
self._max_volume = float(self._device.get_max_volume())
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS) @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
def update(self): def update(self):
"""Retrieve latest state of the TV.""" """Retrieve latest state of the device."""
is_on = self._device.get_power_state() is_on = self._device.get_power_state()
if is_on: if is_on:
@ -91,7 +144,7 @@ class VizioDevice(MediaPlayerDevice):
volume = self._device.get_current_volume() volume = self._device.get_current_volume()
if volume is not None: if volume is not None:
self._volume_level = float(volume) / 100. self._volume_level = float(volume) / self._max_volume
input_ = self._device.get_current_input() input_ = self._device.get_current_input()
if input_ is not None: if input_ is not None:
@ -113,40 +166,40 @@ class VizioDevice(MediaPlayerDevice):
@property @property
def state(self): def state(self):
"""Return the state of the TV.""" """Return the state of the device."""
return self._state return self._state
@property @property
def name(self): def name(self):
"""Return the name of the TV.""" """Return the name of the device."""
return self._name return self._name
@property @property
def volume_level(self): def volume_level(self):
"""Return the volume level of the TV.""" """Return the volume level of the device."""
return self._volume_level return self._volume_level
@property @property
def source(self): def source(self):
"""Return current input of the TV.""" """Return current input of the device."""
return self._current_input return self._current_input
@property @property
def source_list(self): def source_list(self):
"""Return list of available inputs of the TV.""" """Return list of available inputs of the device."""
return self._available_inputs return self._available_inputs
@property @property
def supported_features(self): def supported_features(self):
"""Flag TV features that are supported.""" """Flag device features that are supported."""
return SUPPORTED_COMMANDS return self._supported_commands
def turn_on(self): def turn_on(self):
"""Turn the TV player on.""" """Turn the device on."""
self._device.pow_on() self._device.pow_on()
def turn_off(self): def turn_off(self):
"""Turn the TV player off.""" """Turn the device off."""
self._device.pow_off() self._device.pow_off()
def mute_volume(self, mute): def mute_volume(self, mute):
@ -169,27 +222,27 @@ class VizioDevice(MediaPlayerDevice):
self._device.input_switch(source) self._device.input_switch(source)
def volume_up(self): def volume_up(self):
"""Increasing volume of the TV.""" """Increasing volume of the device."""
self._volume_level += self._volume_step / 100. self._volume_level += self._volume_step / self._max_volume
self._device.vol_up(num=self._volume_step) self._device.vol_up(num=self._volume_step)
def volume_down(self): def volume_down(self):
"""Decreasing volume of the TV.""" """Decreasing volume of the device."""
self._volume_level -= self._volume_step / 100. self._volume_level -= self._volume_step / self._max_volume
self._device.vol_down(num=self._volume_step) self._device.vol_down(num=self._volume_step)
def validate_setup(self): def validate_setup(self):
"""Validate if host is available and key is correct.""" """Validate if host is available and auth token is correct."""
return self._device.get_current_volume() is not None return self._device.get_current_volume() is not None
def set_volume_level(self, volume): def set_volume_level(self, volume):
"""Set volume level.""" """Set volume level."""
if self._volume_level is not None: if self._volume_level is not None:
if volume > self._volume_level: if volume > self._volume_level:
num = int(100*(volume - self._volume_level)) num = int(self._max_volume * (volume - self._volume_level))
self._volume_level = volume self._volume_level = volume
self._device.vol_up(num=num) self._device.vol_up(num=num)
elif volume < self._volume_level: elif volume < self._volume_level:
num = int(100*(self._volume_level - volume)) num = int(self._max_volume * (self._volume_level - volume))
self._volume_level = volume self._volume_level = volume
self._device.vol_down(num=num) self._device.vol_down(num=num)

View File

@ -1459,7 +1459,7 @@ pyvera==0.2.45
pyvesync_v2==0.9.6 pyvesync_v2==0.9.6
# homeassistant.components.vizio # homeassistant.components.vizio
pyvizio==0.0.4 pyvizio==0.0.7
# homeassistant.components.velux # homeassistant.components.velux
pyvlx==0.2.10 pyvlx==0.2.10