Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion homeassistant/components/vizio/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""The vizio component."""

import voluptuous as vol

from homeassistant.const import (
Expand All @@ -8,6 +9,7 @@
CONF_NAME,
)
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.typing import ConfigType

from .const import (
CONF_VOLUME_STEP,
Expand All @@ -17,14 +19,15 @@
)


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:
raise vol.Invalid(
f"When '{CONF_DEVICE_CLASS}' is 'tv' then '{CONF_ACCESS_TOKEN}' is required.",
path=[CONF_ACCESS_TOKEN],
)

return config


Expand Down
1 change: 0 additions & 1 deletion homeassistant/components/vizio/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
DEFAULT_VOLUME_STEP = 1
DEFAULT_DEVICE_CLASS = "tv"
DEVICE_ID = "pyvizio"
DEVICE_NAME = "Python Vizio"

DOMAIN = "vizio"

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/vizio/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
}
128 changes: 80 additions & 48 deletions homeassistant/components/vizio/media_player.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -25,9 +27,12 @@
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__)

Expand All @@ -52,32 +57,43 @@
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
Expand All @@ -88,31 +104,32 @@ def __init__(self, host, token, name, volume_step, device_type):
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]

Expand All @@ -127,100 +144,115 @@ def update(self):
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)
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down