Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define the turn on/off action for media player Kodi with scripts #8408

Closed
wants to merge 7 commits into from
96 changes: 77 additions & 19 deletions homeassistant/components/media_player/kodi.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
https://home-assistant.io/components/media_player.kodi/
"""
import asyncio
from functools import wraps
from collections import OrderedDict
from functools import partial, wraps
import logging
import urllib
import re
Expand All @@ -20,15 +21,18 @@
SUPPORT_PLAY_MEDIA, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, SUPPORT_STOP,
SUPPORT_TURN_OFF, SUPPORT_PLAY, SUPPORT_VOLUME_STEP, SUPPORT_SHUFFLE_SET,
MediaPlayerDevice, PLATFORM_SCHEMA, MEDIA_TYPE_MUSIC, MEDIA_TYPE_TVSHOW,
MEDIA_TYPE_VIDEO, MEDIA_TYPE_PLAYLIST, MEDIA_PLAYER_SCHEMA, DOMAIN)
MEDIA_TYPE_VIDEO, MEDIA_TYPE_PLAYLIST, MEDIA_PLAYER_SCHEMA, DOMAIN,
SUPPORT_TURN_ON)
from homeassistant.const import (
STATE_IDLE, STATE_OFF, STATE_PAUSED, STATE_PLAYING, CONF_HOST, CONF_NAME,
CONF_PORT, CONF_SSL, CONF_PROXY_SSL, CONF_USERNAME, CONF_PASSWORD,
CONF_TIMEOUT, EVENT_HOMEASSISTANT_STOP)
from homeassistant.core import callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers import script, config_validation as cv
from homeassistant.helpers.deprecation import get_deprecated
from homeassistant.helpers.template import Template
from homeassistant.util.yaml import dump

REQUIREMENTS = ['jsonrpc-async==0.6', 'jsonrpc-websocket==0.5']

Expand All @@ -37,6 +41,7 @@
EVENT_KODI_CALL_METHOD_RESULT = 'kodi_call_method_result'

CONF_TCP_PORT = 'tcp_port'
CONF_TURN_ON_ACTION = 'turn_on_action'
CONF_TURN_OFF_ACTION = 'turn_off_action'
CONF_ENABLE_WEBSOCKET = 'enable_websocket'

Expand All @@ -47,7 +52,14 @@
DEFAULT_PROXY_SSL = False
DEFAULT_ENABLE_WEBSOCKET = True

TURN_OFF_ACTION = [None, 'quit', 'hibernate', 'suspend', 'reboot', 'shutdown']
DEPRECATED_TURN_OFF_ACTIONS = {
None: None,
'quit': 'Application.Quit',
'hibernate': 'System.Hibernate',
'suspend': 'System.Suspend',
'reboot': 'System.Reboot',
'shutdown': 'System.Shutdown'
}

# https://github.com/xbmc/xbmc/blob/master/xbmc/media/MediaType.h
MEDIA_TYPES = {
Expand Down Expand Up @@ -75,7 +87,9 @@
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_TCP_PORT, default=DEFAULT_TCP_PORT): cv.port,
vol.Optional(CONF_PROXY_SSL, default=DEFAULT_PROXY_SSL): cv.boolean,
vol.Optional(CONF_TURN_OFF_ACTION, default=None): vol.In(TURN_OFF_ACTION),
vol.Optional(CONF_TURN_ON_ACTION, default=None): cv.SCRIPT_SCHEMA,
vol.Optional(CONF_TURN_OFF_ACTION):
vol.Any(cv.SCRIPT_SCHEMA, vol.In(DEPRECATED_TURN_OFF_ACTIONS)),
vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
vol.Inclusive(CONF_USERNAME, 'auth'): cv.string,
vol.Inclusive(CONF_PASSWORD, 'auth'): cv.string,
Expand Down Expand Up @@ -114,11 +128,36 @@
}


def _check_deprecated_turn_off(hass, turn_off_action):
"""Create an equivalent script for old turn off actions."""
if isinstance(turn_off_action, str):
method = DEPRECATED_TURN_OFF_ACTIONS[turn_off_action]
new_config = OrderedDict(
[('service', '{}.{}'.format(DOMAIN, SERVICE_CALL_METHOD)),
('data_template', OrderedDict(
[('entity_id', '{{ entity_id }}'),
('method', method)]))])
example_conf = dump(OrderedDict(
[(CONF_TURN_OFF_ACTION, new_config)]))
_LOGGER.warning(
"The '%s' action for turn off Kodi is deprecated and "
"will cease to function in a future release. You need to "
"change it for a generic Home Assistant script sequence, "
"which is, for this turn_off action, like this:\n%s",
turn_off_action, example_conf)
new_config['data_template'] = OrderedDict(
[(key, Template(value, hass))
for key, value in new_config['data_template'].items()])
turn_off_action = [new_config]
return turn_off_action


@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the Kodi platform."""
if DATA_KODI not in hass.data:
hass.data[DATA_KODI] = []
name = config.get(CONF_NAME)
host = config.get(CONF_HOST)
port = config.get(CONF_PORT)
tcp_port = config.get(CONF_TCP_PORT)
Expand All @@ -134,10 +173,11 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):

entity = KodiDevice(
hass,
name=config.get(CONF_NAME),
name=name,
host=host, port=port, tcp_port=tcp_port, encryption=encryption,
username=config.get(CONF_USERNAME),
password=config.get(CONF_PASSWORD),
turn_on_action=config.get(CONF_TURN_ON_ACTION),
turn_off_action=config.get(CONF_TURN_OFF_ACTION),
timeout=config.get(CONF_TIMEOUT), websocket=websocket)

Expand Down Expand Up @@ -210,7 +250,8 @@ class KodiDevice(MediaPlayerDevice):
"""Representation of a XBMC/Kodi device."""

def __init__(self, hass, name, host, port, tcp_port, encryption=False,
username=None, password=None, turn_off_action=None,
username=None, password=None,
turn_on_action=None, turn_off_action=None,
timeout=DEFAULT_TIMEOUT, websocket=True):
"""Initialize the Kodi device."""
import jsonrpc_async
Expand Down Expand Up @@ -262,6 +303,17 @@ def on_hass_stop(event):
else:
self._ws_server = None

# Script creation for the turn on/off config options
if turn_on_action is not None:
turn_on_action = script.Script(
self.hass, turn_on_action,
"{} turn ON script".format(self.name),
self.async_update_ha_state(True))
if turn_off_action is not None:
turn_off_action = script.Script(
self.hass, _check_deprecated_turn_off(hass, turn_off_action),
"{} turn OFF script".format(self.name))
self._turn_on_action = turn_on_action
self._turn_off_action = turn_off_action
self._enable_websocket = websocket
self._players = list()
Expand Down Expand Up @@ -304,7 +356,7 @@ def async_on_volume_changed(self, sender, data):

@callback
def async_on_quit(self, sender, data):
"""Handle the muted volume."""
"""Reset the player state on quit action."""
self._players = None
self._properties = {}
self._item = {}
Expand Down Expand Up @@ -520,25 +572,31 @@ def supported_features(self):
"""Flag media player features that are supported."""
supported_features = SUPPORT_KODI

if self._turn_off_action in TURN_OFF_ACTION:
if self._turn_on_action is not None:
supported_features |= SUPPORT_TURN_ON

if self._turn_off_action is not None:
supported_features |= SUPPORT_TURN_OFF

return supported_features

@cmd
@asyncio.coroutine
def async_turn_on(self):
"""Execute turn_on_action to turn on media player."""
if self._turn_on_action is not None:
yield from self._turn_on_action.async_run(
variables={"entity_id": self.entity_id})
else:
_LOGGER.warning("turn_on requested but turn_on_action is none")

@cmd
@asyncio.coroutine
def async_turn_off(self):
"""Execute turn_off_action to turn off media player."""
if self._turn_off_action == 'quit':
yield from self.server.Application.Quit()
elif self._turn_off_action == 'hibernate':
yield from self.server.System.Hibernate()
elif self._turn_off_action == 'suspend':
yield from self.server.System.Suspend()
elif self._turn_off_action == 'reboot':
yield from self.server.System.Reboot()
elif self._turn_off_action == 'shutdown':
yield from self.server.System.Shutdown()
if self._turn_off_action is not None:
yield from self._turn_off_action.async_run(
variables={"entity_id": self.entity_id})
else:
_LOGGER.warning("turn_off requested but turn_off_action is none")

Expand Down