diff --git a/CODEOWNERS b/CODEOWNERS index e5d009cc7a5..a6dd61e4ffb 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -237,6 +237,7 @@ homeassistant/components/uptimerobot/* @ludeeus homeassistant/components/utility_meter/* @dgomes homeassistant/components/velux/* @Julius2342 homeassistant/components/version/* @fabaff +homeassistant/components/vizio/* @raman325 homeassistant/components/waqi/* @andrey-git homeassistant/components/weather/* @fabaff homeassistant/components/weblink/* @home-assistant/core diff --git a/homeassistant/components/vizio/manifest.json b/homeassistant/components/vizio/manifest.json index ac589de841a..c65204d78e8 100644 --- a/homeassistant/components/vizio/manifest.json +++ b/homeassistant/components/vizio/manifest.json @@ -3,8 +3,8 @@ "name": "Vizio", "documentation": "https://www.home-assistant.io/components/vizio", "requirements": [ - "pyvizio==0.0.4" + "pyvizio==0.0.7" ], "dependencies": [], - "codeowners": [] + "codeowners": ["@raman325"] } diff --git a/homeassistant/components/vizio/media_player.py b/homeassistant/components/vizio/media_player.py index 7b47a388325..68374ed59b9 100644 --- a/homeassistant/components/vizio/media_player.py +++ b/homeassistant/components/vizio/media_player.py @@ -1,18 +1,30 @@ -"""Vizio SmartCast TV support.""" +"""Vizio SmartCast Device support.""" from datetime import timedelta import logging - import voluptuous as vol - from homeassistant import util from homeassistant.components.media_player import ( - MediaPlayerDevice, PLATFORM_SCHEMA) + MediaPlayerDevice, + PLATFORM_SCHEMA +) from homeassistant.components.media_player.const import ( - SUPPORT_NEXT_TRACK, SUPPORT_PREVIOUS_TRACK, - SUPPORT_SELECT_SOURCE, SUPPORT_TURN_OFF, SUPPORT_TURN_ON, - SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_STEP) + SUPPORT_NEXT_TRACK, + SUPPORT_PREVIOUS_TRACK, + SUPPORT_SELECT_SOURCE, + SUPPORT_TURN_OFF, + SUPPORT_TURN_ON, + SUPPORT_VOLUME_MUTE, + SUPPORT_VOLUME_SET, + SUPPORT_VOLUME_STEP +) 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 _LOGGER = logging.getLogger(__name__) @@ -22,6 +34,7 @@ CONF_VOLUME_STEP = 'volume_step' DEFAULT_NAME = 'Vizio SmartCast' DEFAULT_VOLUME_STEP = 1 +DEFAULT_DEVICE_CLASS = 'tv' DEVICE_ID = 'pyvizio' DEVICE_NAME = 'Python Vizio' @@ -30,36 +43,71 @@ ICON = 'mdi:television' MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1) MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) -SUPPORTED_COMMANDS = SUPPORT_TURN_ON | SUPPORT_TURN_OFF \ - | SUPPORT_SELECT_SOURCE \ - | SUPPORT_NEXT_TRACK | SUPPORT_PREVIOUS_TRACK \ - | SUPPORT_VOLUME_MUTE | SUPPORT_VOLUME_STEP \ - | SUPPORT_VOLUME_SET +COMMON_SUPPORTED_COMMANDS = ( + SUPPORT_SELECT_SOURCE | + SUPPORT_TURN_ON | + SUPPORT_TURN_OFF | + SUPPORT_VOLUME_MUTE | + SUPPORT_VOLUME_SET | + SUPPORT_VOLUME_STEP +) -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Required(CONF_HOST): cv.string, - vol.Required(CONF_ACCESS_TOKEN): cv.string, - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_SUPPRESS_WARNING, default=False): cv.boolean, - vol.Optional(CONF_VOLUME_STEP, default=DEFAULT_VOLUME_STEP): - vol.All(vol.Coerce(int), vol.Range(min=1, max=10)), -}) +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.Optional(CONF_ACCESS_TOKEN): cv.string, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + 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.All(vol.Coerce(int), vol.Range(min=1, max=10)), + }), + validate_auth, +) def setup_platform(hass, config, add_entities, discovery_info=None): - """Set up the VizioTV media player platform.""" - host = config.get(CONF_HOST) + """Set up the Vizio media player platform.""" + host = config[CONF_HOST] token = config.get(CONF_ACCESS_TOKEN) - name = config.get(CONF_NAME) - volume_step = config.get(CONF_VOLUME_STEP) - - device = VizioDevice(host, token, name, volume_step) + 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 device.validate_setup() is False: - _LOGGER.error("Failed to set up Vizio TV platform, " - "please check if host and API key are correct") + fail_auth_msg = "" + 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 - if config.get(CONF_SUPPRESS_WARNING): + if config[CONF_SUPPRESS_WARNING]: from requests.packages import urllib3 _LOGGER.warning("InsecureRequestWarning is disabled " "because of Vizio platform configuration") @@ -68,22 +116,27 @@ def setup_platform(hass, config, add_entities, discovery_info=None): 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.""" import pyvizio - self._device = pyvizio.Vizio(DEVICE_ID, host, DEFAULT_NAME, token) + self._name = name self._state = None self._volume_level = None self._volume_step = volume_step self._current_input = 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) def update(self): - """Retrieve latest state of the TV.""" + """Retrieve latest state of the device.""" is_on = self._device.get_power_state() if is_on: @@ -91,7 +144,7 @@ class VizioDevice(MediaPlayerDevice): volume = self._device.get_current_volume() 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() if input_ is not None: @@ -113,40 +166,40 @@ class VizioDevice(MediaPlayerDevice): @property def state(self): - """Return the state of the TV.""" + """Return the state of the device.""" return self._state @property def name(self): - """Return the name of the TV.""" + """Return the name of the device.""" return self._name @property def volume_level(self): - """Return the volume level of the TV.""" + """Return the volume level of the device.""" return self._volume_level @property def source(self): - """Return current input of the TV.""" + """Return current input of the device.""" return self._current_input @property def source_list(self): - """Return list of available inputs of the TV.""" + """Return list of available inputs of the device.""" return self._available_inputs @property def supported_features(self): - """Flag TV features that are supported.""" - return SUPPORTED_COMMANDS + """Flag device features that are supported.""" + return self._supported_commands def turn_on(self): - """Turn the TV player on.""" + """Turn the device on.""" self._device.pow_on() def turn_off(self): - """Turn the TV player off.""" + """Turn the device off.""" self._device.pow_off() def mute_volume(self, mute): @@ -169,27 +222,27 @@ class VizioDevice(MediaPlayerDevice): self._device.input_switch(source) def volume_up(self): - """Increasing volume of the TV.""" - self._volume_level += self._volume_step / 100. + """Increasing volume of the device.""" + self._volume_level += self._volume_step / self._max_volume self._device.vol_up(num=self._volume_step) def volume_down(self): - """Decreasing volume of the TV.""" - self._volume_level -= self._volume_step / 100. + """Decreasing volume of the device.""" + self._volume_level -= self._volume_step / self._max_volume self._device.vol_down(num=self._volume_step) 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 def set_volume_level(self, volume): """Set volume level.""" if self._volume_level is not None: 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._device.vol_up(num=num) 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._device.vol_down(num=num) diff --git a/requirements_all.txt b/requirements_all.txt index 316a21da33a..2fb55c9ddd5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1459,7 +1459,7 @@ pyvera==0.2.45 pyvesync_v2==0.9.6 # homeassistant.components.vizio -pyvizio==0.0.4 +pyvizio==0.0.7 # homeassistant.components.velux pyvlx==0.2.10