I would like to control my Apple TV 4 on OH2. I have read in several post’s that it shoud be possible with the Apple Home Kit Binding. Nevertheless in my opinion I have the following comprehension. The Home Kit with the appropriate binding is useful for control these different smart devices, however not for the ATV4 itself.
I’d be interested in setting it up as well and controll the appleTV with openhab. Would you please be able to share some insights (for a linux/python newbee) on how to setup pyatv?
Hi,
I’m running Openhab 2.2 on an RPI3 (openhabian). As I’m controlling my TV via openhab already (LIRC), I’d like to do the same with my apple TV. I’ve never used python, so I just followed the steps, shown on githup for installing pyatv, however something seems to not work:
pip install pyatv
Traceback (most recent call last):
File "/usr/bin/pip", line 9, in <module>
load_entry_point('pip==1.5.6', 'console_scripts', 'pip')()
File "/usr/lib/python2.7/dist-packages/pkg_resources.py", line 356, in load_entry_point
return get_distribution(dist).load_entry_point(group, name)
File "/usr/lib/python2.7/dist-packages/pkg_resources.py", line 2476, in load_entry_point
return ep.load()
File "/usr/lib/python2.7/dist-packages/pkg_resources.py", line 2190, in load
['__name__'])
File "/usr/lib/python2.7/dist-packages/pip/__init__.py", line 74, in <module>
from pip.vcs import git, mercurial, subversion, bazaar # noqa
File "/usr/lib/python2.7/dist-packages/pip/vcs/mercurial.py", line 9, in <module>
from pip.download import path_to_url
File "/usr/lib/python2.7/dist-packages/pip/download.py", line 25, in <module>
from requests.compat import IncompleteRead
ImportError: cannot import name IncompleteRead
you need Python 3.5.2 and above. So you have to install it manually because unfortunately there is no package in repository. So try this.
wget https://www.python.org/ftp/python/3.6.4/Python-3.6.4.tgz
tar xvf Python-3.6.4.tgz
cd Python-3.6.4
sudo ./configure --enable-optimizations
sudo make -j8
sudo make altinstall
Then you should have the option to
pip3.6 install pyatv (maybe with sudo)
After that it is easy to scan atv
atvremote scan
and to send some commands like
atvremote -a (or use loginid plus address) play
But I have no clue how to build a useful listener without increasing load too much.
I’ll play around with it a while. The first thing I noticed though is a quite extrem lag: Between issuing the command on the RPi and actually seeing it on the ATV, it takes arr. 7sec.
I think this is because there is a session send everytime.
Another aspect is the load of RPI. If too many processes are running in parallel maybe it is too much for it.
You can send multiple commands after another like
atvremote -a (or use loginid plus address) play stop top_menu
I prefer to send multiple commands sending it to the background like
atvremote -a (or use loginid plus address) play & atvremote -a (or use loginid plus address) stop & atvremote -a (or use loginid plus address) top_menu &
I would advise to try it and test what is better for you.
HFM
PS: If you use loginid and address it is 3 seconds faster. I believe.
Python 3.5.3 is only needed when running pyatv from the master branch (when cloning directly from GitHub). Running the latest release on pypi (0.3.9) only requires python 3.4.2, so the upgrade isn’t really necessary right now but will be in the future.
I can help out building a small script that listens for push updates and sends the update somewhere if you like? It’s quite easy with the “push updates” API:
You will see lag when using autodiscover, but it should be quite fast if you specify address and login_id manually when running a command. pyatv is not that heavy, so it shouldn’t put much load on the system.
An example:
If my wife starts playing a movie (or if possible pushes the button of the remote to activate apple tv) in the evening the state could be sent via API and openHAB could react on this change. Then it decides due to the luminance to switch on dimmed lights (for tv; a small lamp is already on due to motion). If she pauses the movie the lights are dimmed up to brightness. If play state is set to “Idle” and nothing happens for more than X minutes, then everything is switched off.
Another example:
My wife likes shows like “The good wife”. Sometimes there are several weeks between watching sessions. So she (and the apple tv) forgets about what she saw the last time. If the title and position could be sent to openhab to other fields, openHAB can react saving that. And she can look in her app what she watched the last time.
So what would be great if you could develop it?
Listener who reacts on a change of each of the listed (above) values
Reaction is sending an API request to openHAB
Some examples for GET, POST, PUT, DELETE with Python
Being flashed from your propose and sending thanks in advance!
HFM
The REST-part is not really an expertise of mine, so I can provide some conceptual code for that but you will have to adjust it to include the correct payload and select a proper endpoint. I’m only used to Home Assistant (that’s what I developed pyatv for) and have never used OpenHAB.
A very basic example could look like this:
"""Simple push update listener example."""
import sys
import asyncio
import aiohttp
from pyatv import helpers
class PushListener:
def __init__(self, session):
self.session = session
async def _post(self, playstatus):
# This should correspond to the json data expected by the API
payload = {
'title': playstatus.title
}
# Change address to some API endpoint
async with self.session.post(
'http://somehost/api/test', json=payload) as resp:
# This just prints the response - do something useful here
print('Response: ', await resp.text())
def playstatus_update(self, _, playstatus):
asyncio.ensure_future(self._post(playstatus))
def playstatus_error(self, updater, exception):
print('An error occurred (restarting): {0}'.format(exception))
updater.start(initial_delay=1)
async def _listen_for_push_updates(atv):
print('Starting to listen for updates')
try:
with aiohttp.ClientSession() as session:
atv.push_updater.listener = PushListener(session)
await atv.push_updater.start()
except Exception as ex:
print('Got an error: ', ex)
finally:
await atv.logout()
async def _no_device_found():
print('No Apple TV found', file=sys.stderr)
if __name__ == '__main__':
helpers.auto_connect(_listen_for_push_updates,
not_found=_no_device_found)
You will have to adjust the code in _post to do what you want. Also, if you want to POST to multiple endpoints, you can quite easily refactor to accomplish that. Multiple
I tried 2 days to get it working but I failed. I am too unexperienced with Python. I think I have no clue from the basic concepts.
What I tried
I tried to get a manual connect (instead autoconnect from helpers), that was a success, but then I put _listen_for_push_updates as second param and got lots of errors
Because I did not find out how to send a body and not json, I tried to include another library (python-openhab; https://pypi.python.org/pypi/python-openhab/2.2) to simplify the communication with openHAB; I got a first result but nothing happened on openHAB