Binding for Apple-TV

Tags: #<Tag:0x00007f2fbacdb658>

Thie is the openHAB2 binding for the Apple-TV 3/4.

Check out Binding for Apple-TV for the latest release (beta1) and later posts. Happy to receive your feedback in this post or by PM.

Check the README for additional information.


Note

It integrates the PyATV Python library, which implements the protocol layer. The binding includes also platform specific stuff (jpy Java/Phyton bridge). All modules are included in the binding package and the binding tries to auto-select them. An upcoming version will allow to overwrite this auto-detection in case something went wrong or you have specific installation requirements.

Thanks postlund for his great work in contributing the PyATV library (https://github.com/postlund/pyatv) and the jpy team (https://github.com/bcdev/jpy).


Initial post:
I finished some prototyping to implement a binding for Apple-TV control. The binding will be based pyATV (a Python library). This will be wrapped into the binding using jpy (Java-Python bridge). The binding jar includes a pyatv version, which has a minor change so I could use the same functionality as the cli version supplies.

Target #1 is to implement basic control (top_menu, up, down etc.). This already works with the cli, but pyatv needs to reconnect for any sequence, which takes about 3s = too slow for a convinient UX.
Next step could be retrieving artwork, url etc.

Please feel free to join with ideas etc. I’ll publish the repo link jere once alpha1 is ready to test.

Check ATV4 - Apple TV Binding for discussion around using pyatv

12 Likes

Great :+1:

I’m still waiting for the move back of Eclipssmarthome to openHAB to continue testing Snapshots with your MagentaTV binding and then also the ATV4-binding :grinning:

The most important features to me for this binding would be how I’m currently using pyATV - not to controll the ATV from OpenHAB but to control OpenHAB from the ATV. As in - change the lights based on play/pause/stop/idle state.

Some things I’ve been too lazy to investigate/fix on my basic setup (linked on the other thread above) include the ability to know media type (I don’t need a light show when listening to music). Another thing I’ve noticed lately is the constant play/pause/whatever else state that Netflix does with it’s video ads while you browse. Maybe I should ignore commands for 30-45 seconds if the app is Netflix…

I’m not sure your goal is to implement this setup - it seems like you’re mostly hoping to control the ATV from OH. I’ll be watching here to see where this goes!

1 Like

lets see, what I could achieve. First step is to implement the control functions. Redirecting events to the binding requires the reverse direction Python->Java. This is also supported by jpy, but I didn’t looked into that. Do you know if the event interface is fully asynchronous (some kind of background process) or I need to poll. This has to work in parallel to the cli interface, which I currently use as entry point. My Python knowledge is more or less 0 so don’t expect real code changes/implementation on the Python side or someone needs to help me.

+1

For now I use a Wemos D1 with an IR Diode. Send IR Commands via MQTT.

Works very well, but a binding would be even better :slight_smile:

I looked to the push_update and this sounds achievable, need to do some testing throwing events from the Python side and runnung that as background task (how it‘s already implemented) in parallel to process regular commands.

Hi guys,

I created the repot at https://github.com/markus7017/org.openhab.binding.appletv/tree/alpha1.
This is a very early build. I get it working on macOS, but still some issues on the Raspberry.

Due to the fact that I need jpy to access the Python ATV library the binding jar includes some platform specific modules. The plan ist to detect the platform and install them automatically - already working. In addition I need to set the path to the Python 3.5/3.6 library, which still challenges me to find the correct path and being ahead of specifics of the local installation. I also want to make sure that a list of required Python packages is included, maybe I include an auto-install using pip3.

I’ll be on vacation for the next 2 weeks. Feel free to have a look to the code - contributions welcome :wink: Maybe you could support by compiling jpy 0.10 for Win32, Win64 and Ubuntu.
Checkout jpynon Github for instructions. Make sure to run the script with Python 3.6 or 3.5.

Thanks for your effort. Looking forward to turning the lights on and off depending on the play_state.

1 Like

Hi Guys,

I’m very much a beginner with openHAB, but enjoying learning about it.
I’d like to have a go at the Apple TV binding, but I’m not quite sure how to install it.
I have previously either used bindings that are downloadable directly from the Paper UI, or by putting the jar file in the addon directory.

I couldn’t find a jar file in the git repo… can anyone explain how I can install the binding?

Thanks!

This is the pinnacle feature of my smart home. I still get excited about it every time I watch TV - and I’ve had it functioning for almost a year now.

I‘m currently out-of-country. Please give me some time to provided the read to use build (jar)

ok, I’m back. I added the jar to the repo (keep in mind this is the first alpha).
What platform are you running openHAB? For now the bundle includes jpy platform support for Raspberry (ARM) and macOS (x64). I’m looking forward to support Ubuntu and Windows64, but need you help to build jpy for those platforms, because I don’t have them.

https://github.com/markus7017/org.openhab.binding.appletv/blob/alpha1/target/org.openhab.binding.appletv-2.4.0-SNAPSHOT.jar

Please always use log level set to TRACE for alpha releases.

Thanks for the JAR file!

I’m running OpenHAB on a Debian virtual machine, running on my Synology NAS.
I believe it’s AMD64 architecture.

I’ve installed jpy and pyatv and i’ve paired my AppleTV using atvremote cli and then manually added the thing in PaperUI with the IP and login id. In my list of things the status shows up as “unknown” and it doesnt work…

This shows up in my openhab.log:

2019-02-19 09:58:20.921 [ERROR] [pletv.internal.AppleTVHandlerFactory] - Apple-TV.PyATV: Unable to install PyATV library: null (class java.lang.NullPointerException)
2019-02-19 09:58:20.922 [WARN ] [mmon.WrappedScheduledExecutorService] - Scheduled runnable ended with an exception:
java.lang.NoClassDefFoundError: Could not initialize class org.jpy.PyLib
        at org.openhab.binding.appletv.internal.jpy.LibPyATV.<init>(LibPyATV.java:132) ~[?:?]
        at org.openhab.binding.appletv.internal.handler.AppleTVHandler.lambda$0(AppleTVHandler.java:61) ~[?:?]
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[?:?]
        at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[?:?]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) ~[?:?]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) ~[?:?]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:?]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:?]
        at java.lang.Thread.run(Thread.java:748) [?:?]

If there’s any idea you may have why this is happening, or if you’d like my version of jpy then just let me know.

Thanks!

I need the complete TRACE log. First we need to check the platform info. There should be a line like

"[DEBUG] ... Platform info: '{}', architecture: '{}'

As I noted the binding incorporates jpy, which has platform specific code. We need to build those modules on the platform, so I could include them into the bundle.

Which Python version are you using? 3.5 or 3.6. I’m thinking to limit the support to 3.6.Supporting any combination of platform and Python version does not make sense and would be an overkill for the maintenance.

Currently I’m working to get it running on the Raspberry. The problem with Python is that there could be different versions and packages on the system. I need to fiddle around to find the dependencies so I know where the modules are located, which packages are required and what is possible to mix - not easy :frowning:

Sorry, I’m still very much a beginner with OpenHAB and I’m not sure what you mean by TRACE log.

My current Python version is 3.5.3 (although 2.7.13 also seems to be installed), but I’ll happily upgrade to 3.6 if that makes things easier.

OK, turns out that Python2.7 is a dependency for some other package that i have running…
For Python3 I have now upgraded to 3.6.8 and reinstalled pyatv and jpy.

Still getting the following in my log:

2019-02-21 12:34:31.118 [ERROR] [pletv.internal.AppleTVHandlerFactory] - Apple-TV.PyATV: Unable to install PyATV library: null (class java.lang.NullPointerException)
2019-02-21 12:34:31.119 [WARN ] [mmon.WrappedScheduledExecutorService] - Scheduled runnable ended with an exception:
java.lang.NoClassDefFoundError: Could not initialize class org.jpy.PyLib
        at org.openhab.binding.appletv.internal.jpy.LibPyATV.<init>(LibPyATV.java:132) ~[?:?]
        at org.openhab.binding.appletv.internal.handler.AppleTVHandler.lambda$0(AppleTVHandler.java:61) ~[?:?]
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[?:?]
        at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[?:?]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) ~[?:?]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) ~[?:?]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:?]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:?]
        at java.lang.Thread.run(Thread.java:748) [?:?]

In my logging config i’ve added the following line to try to enable TRACE logging:

log:set TRACE org.openhab.binding.appletv

But i’m not so sure if this actually does the trick…

I think I managed to set the logging to TRACE.
Here’s what i get:

2019-02-22 12:21:47.623 [DEBUG] [pletv.internal.AppleTVHandlerFactory] - Apple-TV.PyATV: Platform info: 'linux', architecture: 'amd64'
2019-02-22 12:21:47.624 [DEBUG] [pletv.internal.AppleTVHandlerFactory] - Apple-TV.PyATV: jpy.jpyLib: /var/lib/openhab2/tmp/ohlib-3456898468252560272/jpy.so
2019-02-22 12:21:47.625 [DEBUG] [pletv.internal.AppleTVHandlerFactory] - Apple-TV.PyATV: jpy.jdlLib: /var/lib/openhab2/tmp/ohlib-3456898468252560272/jdl.so
2019-02-22 12:21:47.626 [DEBUG] [pletv.internal.AppleTVHandlerFactory] - Apple-TV.PyATV: jpy.pythonLib: null
2019-02-22 12:21:47.627 [DEBUG] [pletv.internal.AppleTVHandlerFactory] - Apple-TV.PyATV: jpy.pythonPrefix: /usr
2019-02-22 12:21:47.627 [DEBUG] [pletv.internal.AppleTVHandlerFactory] - Apple-TV.PyATV: jpy.pythonExecutable: /usr/bin/python
2019-02-22 12:21:47.654 [ERROR] [pletv.internal.AppleTVHandlerFactory] - Apple-TV.PyATV: Unable to install PyATV library: null (class java.lang.NullPointerException)
2019-02-22 12:21:47.660 [ERROR] [pletv.internal.AppleTVHandlerFactory] - Apple-TV.PyATV: Unable to install PyATV library: null (class java.lang.NullPointerException)
2019-02-22 12:21:47.662 [ERROR] [pletv.internal.AppleTVHandlerFactory] - Apple-TV.PyATV: Unable to install PyATV library: null (class java.lang.NullPointerException)
2019-02-22 12:21:47.664 [WARN ] [mmon.WrappedScheduledExecutorService] - Scheduled runnable ended with an exception:
java.lang.NoClassDefFoundError: Could not initialize class org.jpy.PyLib
        at org.openhab.binding.appletv.internal.jpy.LibPyATV.<init>(LibPyATV.java:132) ~[?:?]
        at org.openhab.binding.appletv.internal.handler.AppleTVHandler.lambda$0(AppleTVHandler.java:61) ~[?:?]
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[?:?]
        at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[?:?]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) ~[?:?]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) ~[?:?]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:?]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:?]
        at java.lang.Thread.run(Thread.java:748) [?:?]

Is that what you were looking for?

This line also caught my eye:

jpy.pythonExecutable: /usr/bin/python

That executes my python version 2.7 (required for Samba)
I do also have a /usr/bin/python3 which is my python 3.6.8

Thanks!

ok, first we need to find

  • the correct jpy modules
  • the python3 library

The binding requests Python 3, version 2.x is not supported. I’m not sure sure about Python 3.5 or 3.6.

You are right I should set jpy.pythonExecutable to /usr/bin/python3 even I don’t think that the executable is called, because jpy loads the python library dynamically.

It’s all about the right filenames and I still doesn’t found a genric way where to find those modules.
I assume you ran “apt-get install python-jpy”?

I’m not sure if we can use the package python-jpy, but at least we give it a try. I compiled version 0.10 for Mac and Raspberry, but using the pacage would simplify the setup.

Where does the modules go (which path)?
Could you please run

find / -name "jpy*.so"
find / -name "jdl*.so"

Do you have a JDK (not JRE) on the system?
and the GCC dev tools?


UPDATE: Found out that there is a python3-jpy package, please run

apt-get install python3-jpy

afterwards you should find the modules under /usr/lib/python3/dist-packages
in my case

/usr/lib/python3/dist-packages/jpy.cpython-35m-arm-linux-gnueabihf.so and
/usr/lib/python3/dist-packages/jdl.cpython-35m-arm-linux-gnueabihf.so

your’s could look like

/usr/lib/python3/dist-packages/jpy.cpython-35m-amd64-linux-gnueabihf.so and
/usr/lib/python3/dist-packages/jdl.cpython-35m-amd64-linux-gnueabihf.so

(amd64 vs. arm in the filename)

I notice that those modules are Python 3.5, but hopefully it also works with Python 3.6 (having different versions at the same time going to different directories is a little spooky, but that’s Python)

For jpy (and pyatv) installation I used pip; so the command was:

pip3 install jpy
pip3 install pyatv

I do believe that pip sometimes has issues of it’s own and can also be run (pretty fail-safe) in this way:

python3 -m pip install jpy

The path to jpy on my machine is:

/usr/local/lib/python3.6/site-packages/jpy.cpython-36m-x86_64-linux-gnu.so

I do have the GCC dev tools installed and i believe i also have JDK.
At least both the following commands give me some output…

java -version
java version "1.8.0_201"

javac -version
javac 1.8.0_201

UPDATE:

I thought that once a python package is installed you can just import it like so:

import jpy

Without having to know the exact filename & location of it…?

Here is a thought; how about the following bit of code?

import pip

pkgs = ['jpy', 'pyatv']
for package in pkgs:
    try:
        import package
    except ImportError, e:
        pip.main(['install', package])

See if we can import jpy and pyatv, if not then install them using pip.

Probably not a very clean solution, but i think it would work.

UPDATE: A bit cleaner solution

import pip

required_pkgs = ['jpy', 'pyatv']
installed_pkgs = [pkg.key for pkg in pip.get_installed_distributions()]

for package in required_pkgs:
    if package not in installed_pkgs:
        pip.main(['install', package])