Add generic command/button functionality to webostv (#30379)

* add generic command/button functionality to webostv

* update codeowners
This commit is contained in:
Josh Bendavid 2020-01-02 16:32:57 -05:00 committed by springstan
parent 9064058a03
commit c1936f6fe4
6 changed files with 124 additions and 3 deletions

View File

@ -365,6 +365,7 @@ homeassistant/components/waqi/* @andrey-git
homeassistant/components/watson_tts/* @rutkai
homeassistant/components/weather/* @fabaff
homeassistant/components/weblink/* @home-assistant/core
homeassistant/components/webostv/* @bendavid
homeassistant/components/websocket_api/* @home-assistant/core
homeassistant/components/wemo/* @sqldiablo
homeassistant/components/withings/* @vangorra

View File

@ -7,6 +7,7 @@ import voluptuous as vol
from websockets.exceptions import ConnectionClosed
from homeassistant.const import (
ATTR_ENTITY_ID,
CONF_CUSTOMIZE,
CONF_HOST,
CONF_ICON,
@ -14,6 +15,7 @@ from homeassistant.const import (
EVENT_HOMEASSISTANT_STOP,
)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import async_dispatcher_send
DOMAIN = "webostv"
@ -23,6 +25,12 @@ CONF_STANDBY_CONNECTION = "standby_connection"
DEFAULT_NAME = "LG webOS Smart TV"
WEBOSTV_CONFIG_FILE = "webostv.conf"
SERVICE_BUTTON = "button"
ATTR_BUTTON = "button"
SERVICE_COMMAND = "command"
ATTR_COMMAND = "command"
CUSTOMIZE_SCHEMA = vol.Schema(
{vol.Optional(CONF_SOURCES, default=[]): vol.All(cv.ensure_list, [cv.string])}
)
@ -50,6 +58,17 @@ CONFIG_SCHEMA = vol.Schema(
extra=vol.ALLOW_EXTRA,
)
CALL_SCHEMA = vol.Schema({vol.Required(ATTR_ENTITY_ID): cv.comp_entity_ids})
BUTTON_SCHEMA = CALL_SCHEMA.extend({vol.Required(ATTR_BUTTON): cv.string})
COMMAND_SCHEMA = CALL_SCHEMA.extend({vol.Required(ATTR_COMMAND): cv.string})
SERVICE_TO_METHOD = {
SERVICE_BUTTON: {"method": "async_button", "schema": BUTTON_SCHEMA},
SERVICE_COMMAND: {"method": "async_command", "schema": COMMAND_SCHEMA},
}
_LOGGER = logging.getLogger(__name__)
@ -57,6 +76,18 @@ async def async_setup(hass, config):
"""Set up the LG WebOS TV platform."""
hass.data[DOMAIN] = {}
async def async_service_handler(service):
method = SERVICE_TO_METHOD.get(service.service)
data = service.data.copy()
data["method"] = method["method"]
async_dispatcher_send(hass, DOMAIN, data)
for service in SERVICE_TO_METHOD:
schema = SERVICE_TO_METHOD[service]["schema"]
hass.services.async_register(
DOMAIN, service, async_service_handler, schema=schema
)
tasks = [async_setup_tv(hass, config, conf) for conf in config[DOMAIN]]
if tasks:
await asyncio.gather(*tasks)

View File

@ -6,5 +6,5 @@
"aiopylgtv==0.2.4"
],
"dependencies": ["configurator"],
"codeowners": []
"codeowners": ["@bendavid"]
}

View File

@ -24,13 +24,16 @@ from homeassistant.components.media_player.const import (
SUPPORT_VOLUME_STEP,
)
from homeassistant.const import (
ATTR_ENTITY_ID,
CONF_CUSTOMIZE,
CONF_HOST,
CONF_NAME,
ENTITY_MATCH_ALL,
STATE_OFF,
STATE_PAUSED,
STATE_PLAYING,
)
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.script import Script
from . import CONF_ON_ACTION, CONF_SOURCES, DOMAIN
@ -131,7 +134,9 @@ class LgWebOSMediaPlayerEntity(MediaPlayerDevice):
self._last_icon = None
async def async_added_to_hass(self):
"""Connect and subscribe to state updates."""
"""Connect and subscribe to dispatcher signals and state updates."""
async_dispatcher_connect(self.hass, DOMAIN, self.async_signal_handler)
await self._client.register_state_update_callback(
self.async_handle_state_update
)
@ -144,6 +149,17 @@ class LgWebOSMediaPlayerEntity(MediaPlayerDevice):
"""Call disconnect on removal."""
self._client.unregister_state_update_callback(self.async_handle_state_update)
async def async_signal_handler(self, data):
"""Handle domain-specific signal by calling appropriate method."""
entity_ids = data[ATTR_ENTITY_ID]
if entity_ids == ENTITY_MATCH_ALL or self.entity_id in entity_ids:
params = {
key: value
for key, value in data.items()
if key not in ["entity_id", "method"]
}
await getattr(self, data["method"])(**params)
async def async_handle_state_update(self):
"""Update state from WebOsClient."""
self._current_source_id = self._client.current_appId
@ -406,3 +422,13 @@ class LgWebOSMediaPlayerEntity(MediaPlayerDevice):
await self._client.channel_down()
else:
await self._client.rewind()
@cmd
async def async_button(self, button):
"""Send a button press."""
await self._client.button(button)
@cmd
async def async_command(self, command):
"""Send a command."""
await self._client.request(command)

View File

@ -0,0 +1,26 @@
# Describes the format for available webostv services
button:
description: 'Send a button press command.'
fields:
entity_id:
description: Name(s) of the webostv entities where to run the API method.
example: 'media_player.living_room_tv'
button:
description: Name of the button to press. Known possible values are
LEFT, RIGHT, DOWN, UP, HOME, BACK, ENTER, DASH, INFO, ASTERISK, CC, EXIT,
MUTE, RED, GREEN, BLUE, VOLUMEUP, VOLUMEDOWN, CHANNELUP, CHANNELDOWN,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
example: 'LEFT'
command:
description: 'Send a command.'
fields:
entity_id:
description: Name(s) of the webostv entities where to run the API method.
example: 'media_player.living_room_tv'
command:
description: Endpoint of the command. Known valid endpoints are listed in
https://github.com/TheRealLink/pylgtv/blob/master/pylgtv/endpoints.py
example: 'media.controls/rewind'

View File

@ -9,7 +9,13 @@ from homeassistant.components.media_player.const import (
ATTR_MEDIA_VOLUME_MUTED,
SERVICE_SELECT_SOURCE,
)
from homeassistant.components.webostv import DOMAIN
from homeassistant.components.webostv import (
ATTR_BUTTON,
ATTR_COMMAND,
DOMAIN,
SERVICE_BUTTON,
SERVICE_COMMAND,
)
from homeassistant.const import (
ATTR_ENTITY_ID,
CONF_HOST,
@ -75,3 +81,34 @@ async def test_select_source_with_empty_source_list(hass, client):
assert hass.states.is_state(ENTITY_ID, "playing")
client.launch_app.assert_not_called()
client.set_input.assert_not_called()
async def test_button(hass, client):
"""Test generic button functionality."""
await setup_webostv(hass)
data = {
ATTR_ENTITY_ID: ENTITY_ID,
ATTR_BUTTON: "test",
}
await hass.services.async_call(DOMAIN, SERVICE_BUTTON, data)
await hass.async_block_till_done()
client.button.assert_called_once()
client.button.assert_called_with("test")
async def test_command(hass, client):
"""Test generic button functionality."""
await setup_webostv(hass)
data = {
ATTR_ENTITY_ID: ENTITY_ID,
ATTR_COMMAND: "test",
}
await hass.services.async_call(DOMAIN, SERVICE_COMMAND, data)
await hass.async_block_till_done()
client.request.assert_called_with("test")