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

Conversation

azogue
Copy link
Member

@azogue azogue commented Jul 8, 2017

Description:

Support for generic Home Assistant script sequences to be defined as turn_on_action and turn_off_action parameters for the media player Kodi.

Example entry for configuration.yaml:

media_player:
- platform: kodi
  host: 192.168.1.XX
  turn_on_action:
    - service: wake_on_lan.send_magic_packet
      data:
        mac: B8:27:EB:BD:A4:28
        broadcast_address: 192.168.255.255
    - service: persistent_notification.create
      data:
        message: 'Turn on Kodi with Wake On LAN'
  turn_off_action:
    service: python_script.turn_off_kodi

For the old way of defining the turn_off_action (quit, hibernate, suspend, reboot, and shutdown), a conversion to the script method is performed, and a deprecation warning is logged.

# Equivalent to `turn_off_action: quit`
  turn_off_action:
    service: media_player.kodi_call_method
    data:
      entity_id: media_player.kodi
      method: Application.Quit

Add turn_on and turn_off actions to the media player Kodi to be able to:
- Wake on LAN a device with media_player/turn_on.
- Run a script or a python_script with media_player/turn_on or media_player/turn_off.

Pull request in home-assistant.github.io with documentation (if applicable): home-assistant/home-assistant.io#2951

Checklist:

If user exposed functionality or configuration variables are added/changed:

If the code does not interact with devices:

  • Local tests with tox run successfully. Your PR cannot be merged unless tests pass

More info

This PR is related to the previous work of @tadly in #8240, but with the new approach discussed there, with a new component wake_on_lan (proposed in #8397) which is called from Kodi.

Because it relies on the wake_on_lan component, which is not merged at this time, it can't work now (ImportErrors). If #8397 is not accepted, I will change this accordingly.

The final approach has been to add the turn_on_action and turn_off_action as generic script sequences, deprecating the old turn off options list.

@mention-bot
Copy link

@azogue, thanks for your PR! By analyzing the history of the files in this pull request, we identified @armills, @fabaff and @ettisan to be potential reviewers.

@@ -75,12 +86,17 @@
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_ON_ACTION, default=None): vol.In(TURN_ON_ACTION),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not try to be smart. We should just support the Script syntax. They can do WoL if they want, or call anything else. Use cv.SCRIPT_SCHEMA for validation.

Once validates as SCRIPT_SCHEMA, just make it a script like this: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/alexa.py#L125-L126 and call that during turn_on

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so I'll remove the wake_on_lan option of the turn_on_action list, and the related parameters (mac and wake_on_lan_broadcast). I did it with the spirit to simplify the original reason of #8240, which was to implement WOL to turn Kodi on, and do it like the existent list of posible actions to turn it off.

Do you want me to remove this list and validate directly turn_on_action as cv.SCRIPT_SCHEMA? I thought of that validation for the new params script_on and script_off (instead of cv.entity_id), but writing an entity name sounded cleaner for me than write the script syntax inside the Kodi media player yaml config.
And doing so, what happens with the new option script to turn Kodi off?
I could change the script_on and script_off parameters to cv.SCRIPT_SCHEMA and maintain the turn_on_action list.

Other reason to implement explicitly the WOL option was as proof of concept for the change of WOL dependent platforms, because I'm calling the wake_on_lan setup if needed but not present. This way, the user don't need to define the wake_on_lan: component if using WOL from any platform, so no future breaking changes in these. What do you think, @balloob?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed entity names for cv.SCRIPT_SCHEMA validation, but the symmetry lover that I am makes me keep the turn_on_action list, like the existent turn_off_action list, and ables to implement the script_off action as well, as one more option to the existent list, so no breaking change.
How do you see it now, @balloob?

Copy link
Contributor

@emlove emlove left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @balloob here. The big difference is that turn_off is running methods available from the Kodi API, whereas turn_on is just running actions that are otherwise available from hass.

I think the asymmetry is just a necessary side-effect of the fact that we have to try and turn on a Kodi instance we can't interact with directly. It's still better than the current universal media player workaround.

@azogue
Copy link
Member Author

azogue commented Jul 10, 2017

Ok @armills, @balloob, so, if I understand correctly, you prefer this:

media_player:
- platform: kodi
  host: 192.168.1.XX
  turn_on_action:
    - service: wake_on_lan.send_magic_packet
      data:
        mac: B8:27:EB:BD:A4:28
        broadcast_address: 192.168.255.255
    - service: persistent_notification.create
      data:
        message: 'Turn on Kodi with Wake On LAN'
  turn_off_action: quit

And no script option for the turn_off_action, than this way:

media_player:
- platform: kodi
  host: 192.168.1.XX
  turn_on_action: script
  script_on:
    - service: wake_on_lan.send_magic_packet
      data:
        mac: B8:27:EB:BD:A4:28
        broadcast_address: 192.168.255.255
    - service: persistent_notification.create
      data:
        message: 'Turn on Kodi with Wake On LAN'
  turn_off_action: script
  script_off:
    - service: media_player.kodi_call_method
      data:
        entity_id: media_player.kodi
        method: Addons.ExecuteAddon
        addonid: script.json-cec
        params:
          command: standby
    - service: homeassistant.turn_off
      entity_id: group.restroom
    - service: persistent_notification.create
      data:
        message: 'Turn off Kodi'

Am I correct? I kinda like more the original proposal (with entity names or script_schema), but I'll do what you guys decide.
Please confirm

@azogue
Copy link
Member Author

azogue commented Jul 10, 2017

Another thought:

The turn_on_action option list, with the explicit option wake_on_lan, was launching the setup of the new component wake_on_lan (#8397) if not defined by the user. I wasn't sure of this approach, but the intention was to be able to change the platforms that are using WOL (in other PR), to call the new component but with any breaking change in the user config. Don't know if this is the way you want to go...

@emlove
Copy link
Contributor

emlove commented Jul 10, 2017

So now that I'm looking at this more, a big reason the original turn_off_action list was necessary was that we didn't have the service to call arbitrary methods on the API.

What do you think about eliminating the selection list from turn_off_action, and just using the script syntax here as well? It will be a breaking change, but it will also end up giving the user complete control over how it works, and should make the hass end of things simpler and symmetric. We'd want to make sure we write up really good migration docs in this case.

@azogue
Copy link
Member Author

azogue commented Jul 10, 2017

@armills, you mean like this?:

 turn_off_action:
    - service: media_player.kodi_call_method
      data:
        entity_id: media_player.kodi
        method: System.Hibernate

I'll give it a try, but my first thought is that the user is going to see this as an ugly change, right?
because now the equivalent is simply:

 turn_off_action: hibernate

I don't know...

@emlove
Copy link
Contributor

emlove commented Jul 10, 2017

Yeah, that's how it would look with that proposal. It's certainly more verbose, but having the full flexibility of the script syntax there will be really powerful. I think if we didn't have any turn_off_action today, that's how we would implement it.

@azogue
Copy link
Member Author

azogue commented Jul 10, 2017

Too much verbose for my taste, but I'll do it if this is the preferred method.

Where can I see a sample of the migration instructions for a breaking change like this, to document it properly? Because this will break almost all Kodi configs.

@emlove
Copy link
Contributor

emlove commented Jul 10, 2017

There's a few ways to do it. The nicest way is to try and handle both configuration options temporarily so we can give users a deprecation warning if they're using the old setup, and eventually remove it.

The breaking changes are usually documented in the release notes, and it could potentially be written up as it's own blog post as well. If you go through the history of https://home-assistant.io/blog/ you should be able to find some examples.

It probably is also a good idea to recruit some second opinions from the dev chat, or wait to let @balloob weigh in.

@pvizeli
Copy link
Member

pvizeli commented Jul 10, 2017

media_player:
- platform: kodi
  host: 192.168.1.XX
  turn_on_action:
    - service: wake_on_lan.send_magic_packet
      data:
        mac: B8:27:EB:BD:A4:28
        broadcast_address: 192.168.255.255
    - service: persistent_notification.create
      data:
        message: 'Turn on Kodi with Wake On LAN'

looks greate instead the origin from initial post.

@azogue
Copy link
Member Author

azogue commented Jul 11, 2017

ok @armills, @balloob, @pvizeli , so I've changed that, removed the WOL option, and made turn_on_action and turn_off_action script schemas.

I'm reading the old mode (turn_off_action list of options) and generating a equivalent script, and logging a warning like this:

2017-07-11 14:10:40 WARNING (MainThread) [homeassistant.components.media_player.kodi] The 'quit' 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:
turn_off_action:
  service: media_player.kodi_call_method
  data:
    entity_id: media_player.kodi
    method: Application.Quit

There is a little problem with the method that generates the equivalent script: it needs the entity name to make the script, and in setup it's not present (am I wrong?). I'm slugifying the name to generate it, so it has to work in almost every case, isn't it? In any case, that method only has to live a few updates.

Even so, this needs to be marked as breaking-change for the users to look at.
In the docs I would include an example to configure the turn on action with Wake on LAN, as that was the original reason for this PR, ok?

@emlove
Copy link
Contributor

emlove commented Jul 11, 2017

Awesome work with the deprecation logic!

As far as the entity_id problem, you could move the deprecation check into the async_added_to_hass coroutine. The entity_id will be available at this point.

Example: https://github.com/home-assistant/home-assistant/blob/3ee4d1060fdbe067c19f7d4ce4777a7eb9dbb412/homeassistant/components/input_boolean.py#L141-L149

@azogue
Copy link
Member Author

azogue commented Jul 11, 2017

Ok! Thanks, I didn't know about this async_added_to_hass
I thought of moving the deprecation check to the first script run, but making the conversion at setup looked better. I'll try again!

@pvizeli
Copy link
Member

pvizeli commented Jul 11, 2017

@azogue you can pass the entity_id of calling device with variable to script and there you can use it with template like {{ entity_id }} and as variable={"entity_id": self.entity_id}

…tine, `async_on_quit` now is also a coroutine and triggers when turn_off script is executed
@azogue
Copy link
Member Author

azogue commented Jul 12, 2017

@pvizeli, sorry, I did the change that @armills suggested before you commented. And I didn't understand your message very well in the context of the script creation for the deprecated options...

@armills, I moved the script creation and the deprecation check to async_added_to_hass, and also removed the callbacks to some API calls for the web socket client when quitting, to call async_on_quit as a coroutine, with every call to the turn_off action. All is working as expected.

BUT, when looking at the code, I remembered an issue the all-day running Kodi machines have with the HA implementation: we never see the off state, because, even if quitting Kodi with the turn_off_action, OpenElec / OSMC/ etc restart the Kodi instance, showing rapidly the idle state, which is fine except if you want to use the media player as a switch (for homebridge-homeassistant, for example).

So I made 2 commits, being the second (which I can reset for proposing other PR or simply forget about) an introduction of a internal flag to represent the media player state as a boolean, to make real (and more intuitive) the off state of a 24/7 Kodi device, and be able to turn on / off the attached TV using directly the media_player as a switch**.
For Kodi players who are not running all time, this change shouldn't break anything.

What do you think about?

** To use the Kodi media player as a switch using the turn on / off actions, a little fix in homebridge-homeassistant is needed, because the logic to select the switch behaviour for media players is so strange. I've proposed a PR (#183) with the changes needed, and I'm testing it satisfactory in my production server ;-)

*** The WAF of this change is very high, in my case. One switch less, intuitive option to turn on/off the TV (the big standby button in the frontend), and direct Siri-HomeKit integration.

@emlove
Copy link
Contributor

emlove commented Jul 12, 2017

It's not necessary. The history in the PR is nice to have, and we'll squash it with GitHub when merging into dev.

@azogue
Copy link
Member Author

azogue commented Jul 12, 2017

ok, I'll edit the title and description anyway.

@azogue azogue changed the title Add turn_on/off_actions to Kodi: wake_on_lan and script Define the turn on/off action for media player Kodi with scripts Jul 12, 2017
Copy link
Member

@pvizeli pvizeli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look into google cast, that handle it better with 24/7 players.

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()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add variable={"entity_id": self.entity_id} as param

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I got it, so, if changing the template in the warning to:

turn_off_action:
  service: media_player.kodi_call_method
  data_template:
    entity_id: '{{ entity_id }}'
    method: System.Suspend

there is no need for using the async_added_to_hass coroutine, isn't it? I'll move the script creation to the setup again

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ups, no, I can't. Now I need the async_update_ha_state and async_on_quit entity methods to create the scripts... I'll simply change the template and move out the deprecation conversion

"""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()
yield from self.async_update_ha_state(force_refresh=True)
Copy link
Member

@pvizeli pvizeli Jul 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'll do it as well with the async_on_quit call for the turn_off_action script, ok?

self.hass, self._turn_on_action, "Kodi Turn ON action script")
if self._turn_off_action is not None:
# Deprecation check
if isinstance(self._turn_off_action, str):
Copy link
Member

@pvizeli pvizeli Jul 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should make a hard breaking change. it is only a platform.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean to disable Kodi for almost everyone with the update? I respectfully disagree. IMHO, the warning will be sufficiently explicit for the user to see it and will made the change easily by copy/pasting from the log, everything without the need to see a bad Kodi setup and restart after the obligated analysis. These are very precious minutes, for me at last.

I know the # of users is not in the equation here to decide this, but, off-topic-ing a little only because curiosity, do we now already some big numbers about the components and platforms penetration, from the stats collected by the updater component? I know it's so early for this, but I'm a very curious guy ;-)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with @azogue . Backwards compatibility wouldn't be a requirement to make this change, but if it's already written I'd prefer to keep it to make the transition smooth for everyone.

@azogue
Copy link
Member Author

azogue commented Jul 12, 2017

@pvizeli, about the internal bin state (already removed from this PR):

Look into google cast, that handle it better with 24/7 players.

I don't own one of these, so maybe I don't understand the implementation, but, as I see, a differentiation is done between a self.media_status.player_is_idle and a self.cast.is_idle, as if the activation/deactivation of the device is done with some app even when the device is connected 24/7, is it?

I don't think it's the same problem, because Kodi running 24/7 attached to the TV doesn't know if the TV is turned on (can't pull that state from anywhere, as I know), so is always running and presenting the idle state to HA. Tricks here are done by killing Kodi some way, in order to trigger some CEC actions, but it's a rude way.

My idea was to simply mark a binary state to represent the state of turn_on/off actions, to set the 'off' state to perdure until the next turn_on, or if the Kodi state changes to play something.

The implementation works for the web socket connection (which now perdures between HA on/off cycles by default), and for the polling mode of update, and because Kodi instances which really die on turn_off will appear like before, the change doesn't touch them.

I'll present another PR with this

Copy link
Contributor

@emlove emlove left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got a chance to go over this in more detail. Great job handling all of the deprecation logic at setup time, so it doesn't spread through the rest of the code.

Just a few small requests here. It looks like some of the changes relating to the alternate state tracking are still here. Let's keep this PR limited to the action changes.

@@ -249,9 +286,6 @@ def __init__(self, hass, name, host, port, tcp_port, encryption=False,
self._ws_server.Player.OnStop = self.async_on_stop
self._ws_server.Application.OnVolumeChanged = \
self.async_on_volume_changed
self._ws_server.System.OnQuit = self.async_on_quit
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't get rid of these handlers. They're still used to track the state of Kodi. This is probably part of the second PR.

def async_on_quit(self, sender, data):
"""Handle the muted volume."""
@asyncio.coroutine
def async_on_quit(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change this back to a callback. Docstring fix is a nice catch though. 😳

turn_on_action = script.Script(
self.hass, turn_on_action,
"Kodi Turn ON action script",
partial(self.async_update_ha_state, force_refresh=True))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since async_update_ha_state is a coroutine, you shouldn't need to use partial here. You can just pass it as self.async_update_ha_state(True). It won't get executed until Script schedules the job.

if turn_on_action is not None:
turn_on_action = script.Script(
self.hass, turn_on_action,
"Kodi Turn ON action script",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to incorporate the entity name here? Maybe even as simple as

"{} turn ON script".format(self.name)

if turn_off_action is not None:
turn_off_action = script.Script(
self.hass, _check_deprecated_turn_off(turn_off_action),
"Kodi Turn OFF action script", self.async_on_quit)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't include async_on_quit here. We want to issue the command and rely on Kodi to report the change instead of assuming it happens.

Copy link
Contributor

@emlove emlove left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really like how this turned out. This pattern could end up being really useful for other similar platforms.

@emlove
Copy link
Contributor

emlove commented Jul 14, 2017

Seems travis doesn't want to run either.

@azogue
Copy link
Member Author

azogue commented Jul 19, 2017

@pvizeli, Is there anything else you want me to change in this PR?

@emlove
Copy link
Contributor

emlove commented Jul 19, 2017

I think @pvizeli's concerns have been addressed, but we do need to have Travis run before we can merge. You might need to open a new PR from this branch since it doesn't seem to be triggering here.

@azogue
Copy link
Member Author

azogue commented Jul 19, 2017

@armills, I've opened a new one to try again (#8558)

@emlove
Copy link
Contributor

emlove commented Jul 19, 2017

Thanks! Looks like that one is working.

@emlove emlove closed this Jul 19, 2017
@home-assistant home-assistant locked and limited conversation to collaborators Oct 20, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants