@magnuslsjoberg this can be a great solution! Can you explain the path you followed to integrate pyatv in HABApp?
HABApp
- Follow habappās installation instructions and install habapp in a virtual python environment.
- Implement a habapp test rule and verify installation.
- Set up habapp to start at boot.
pyatv
- Activate the habapp virtual environment.
- Install pyatv in that environment following the instructions for pyatv.
- Run āatvremoteā to verify the setup.
HABApp / pyatv
Please note that this is a only simple way to get it to work! I used the āatvremoteā code as
inspiration but didnāt understand all of it. I am certain that someone with greater python skills
could integrate this in a better way. The code below is trimmed down but should work.
NOTE! The indentation got messed up when I paste the code here (donāt know what Iām doing wrong)
import HABApp
from HABApp.openhab.events import ItemCommandEvent
import logging
import asyncio
import pyatv
class AppleTV(HABApp.Rule):
def __init__(self):
super().__init__()
# Get the event loop for asyncio
self.loop = HABApp.core.const.loop
# No Apple TV connected yet
self.atv = None
# Listen to control commands from openHAB (from proxy String AppleTV_Control)
self.listen_event( 'AppleTV_Control', self.control, ItemCommandEvent )
# Scan and connect
self.run_soon(self.connect)
# Poll status every 5s
self.run_every(None,5,self.status)
async def connect(self):
log.info("Discovering devices on network...")
atvs = await pyatv.scan(self.loop, timeout=20)
if atvs :
# Only got one AppleTV
log.info("Connecting to {0}".format(atvs[0].address))
self.atv = await pyatv.connect(atvs[0],self.loop)
log.info(self.atv.device_info)
else :
log.error("No device found")
def control(self,event):
assert isinstance(event, ItemCommandEvent), type(event)
if self.atv :
# Let the async function 'handle_command' handle this later
self.run_soon(self.handle_command, str(event.value))
async def handle_command(self,command) :
# In 'atvremote' this is much more elegant!
if command == 'select' :
await self.atv.remote_control.select()
# elif ...
#... list all commands handled
elif command == 'home_hold' :
await self.atv.remote_control.home(action=pyatv.const.InputAction.Hold)
async def status(self) :
if self.atv :
status = await self.atv.metadata.playing()
log.info(f'Playing:\n{status}')
app = self.atv.metadata.app
log.info(f'App: {app}')
# Rule initialization
log = logging.getLogger('HABApp')
AppleTV()
Great guide @magnuslsjoberg! Thanks a lot.
@Rickytr You are welcome!
I find both HABApp and pyatv very robust and both projects have excellent documentation.
If you find a way to handle the commands better (instead of listing all of them) or if you
find out how to get push updates from pyatv rather than polling, please report here!
Hi markus7017 and thank you very much for all your work!
Is there any way to get this working with OH3?
Thank you.
Hi everyon,
+1
@magnuslsjoberg I tried to follow your instructions and I can run HABApp from commandline and it is loading the rules files, but I get some errors and have no idea where to start:
[2021-02-23 10:45:17,687] [ HABApp.Rules] ERROR | Error "name 'self' is not defined" in load:
[2021-02-23 10:45:17,688] [ HABApp.Rules] ERROR | Could not load /etc/openhab/habapp/rules/test.py!
[2021-02-23 10:45:17,688] [ HABApp.Rules] ERROR | File "/opt/habapp/lib/python3.7/site-packages/HABApp/rule_manager/rule_file.py", line 79, in load
[2021-02-23 10:45:17,688] [ HABApp.Rules] ERROR | self.create_rules(created_rules)
[2021-02-23 10:45:17,688] [ HABApp.Rules] ERROR | File "/opt/habapp/lib/python3.7/site-packages/HABApp/rule_manager/rule_file.py", line 68, in create_rules
[2021-02-23 10:45:17,689] [ HABApp.Rules] ERROR | '__HABAPP__RULES': created_rules,
[2021-02-23 10:45:17,689] [ HABApp.Rules] ERROR | File "/usr/lib/python3.7/runpy.py", line 263, in run_path
[2021-02-23 10:45:17,689] [ HABApp.Rules] ERROR | pkg_name=pkg_name, script_name=fname)
[2021-02-23 10:45:17,689] [ HABApp.Rules] ERROR | File "/usr/lib/python3.7/runpy.py", line 96, in _run_module_code
[2021-02-23 10:45:17,690] [ HABApp.Rules] ERROR | mod_name, mod_spec, pkg_name, script_name)
[2021-02-23 10:45:17,690] [ HABApp.Rules] ERROR | File "/usr/lib/python3.7/runpy.py", line 85, in _run_code
[2021-02-23 10:45:17,690] [ HABApp.Rules] ERROR | exec(code, run_globals)
[2021-02-23 10:45:17,690] [ HABApp.Rules] ERROR | File "/etc/openhab/habapp/rules/test.py", line 7, in test.py
[2021-02-23 10:45:17,691] [ HABApp.Rules] ERROR | 3 import logging
[2021-02-23 10:45:17,691] [ HABApp.Rules] ERROR | 4 import asyncio
[2021-02-23 10:45:17,691] [ HABApp.Rules] ERROR | 5 import pyatv
[2021-02-23 10:45:17,691] [ HABApp.Rules] ERROR | 6
[2021-02-23 10:45:17,692] [ HABApp.Rules] ERROR | --> 7 class AppleTV(HABApp.Rule):
[2021-02-23 10:45:17,692] [ HABApp.Rules] ERROR | 8
[2021-02-23 10:45:17,692] [ HABApp.Rules] ERROR | ..................................................
[2021-02-23 10:45:17,692] [ HABApp.Rules] ERROR | logging = <module 'logging' from '/usr/lib/python3.7/logging/__init__.py'>
[2021-02-23 10:45:17,693] [ HABApp.Rules] ERROR | asyncio = <module 'asyncio' from '/usr/lib/python3.7/asyncio/__init__.py'>
[2021-02-23 10:45:17,694] [ HABApp.Rules] ERROR | pyatv = <module 'pyatv' from '/opt/habapp/lib/python3.7/site-packages/pyatv/__init__.py'>
[2021-02-23 10:45:17,694] [ HABApp.Rules] ERROR | HABApp.Rule = <class 'HABApp.rule.rule.Rule'>
[2021-02-23 10:45:17,694] [ HABApp.Rules] ERROR | ..................................................
[2021-02-23 10:45:17,694] [ HABApp.Rules] ERROR |
[2021-02-23 10:45:17,695] [ HABApp.Rules] ERROR | File "/etc/openhab/habapp/rules/test.py", line 13, in AppleTV
[2021-02-23 10:45:17,695] [ HABApp.Rules] ERROR | 9 def __init__(self):
[2021-02-23 10:45:17,695] [ HABApp.Rules] ERROR | 10 super().__init__()
[2021-02-23 10:45:17,695] [ HABApp.Rules] ERROR | 11
[2021-02-23 10:45:17,696] [ HABApp.Rules] ERROR | 12 # Get the event loop for asyncio
[2021-02-23 10:45:17,696] [ HABApp.Rules] ERROR | --> 13 self.loop = HABApp.core.const.loop
[2021-02-23 10:45:17,696] [ HABApp.Rules] ERROR | 14
[2021-02-23 10:45:17,697] [ HABApp.Rules] ERROR | ..................................................
[2021-02-23 10:45:17,697] [ HABApp.Rules] ERROR | __init__ = <function 'AppleTV.__init__' test.py:9>
[2021-02-23 10:45:17,697] [ HABApp.Rules] ERROR | HABApp.core.const.loop = <_UnixSelectorEventLoop running=True closed=False debug=True>
[2021-02-23 10:45:17,697] [ HABApp.Rules] ERROR | ..................................................
[2021-02-23 10:45:17,697] [ HABApp.Rules] ERROR |
[2021-02-23 10:45:17,698] [ HABApp.Rules] ERROR | NameError: name 'self' is not defined
When I try to set HABApp to run as a service, the log file does not show anything when I start it but that is a separate topic I guess.
Hi Ronny!
As I wrote in my post:
NOTE! The indentation got messed up when I paste the code here (donāt know what Iām doing wrong)
The init should be:
def __init__(self):
super().__init__()
# Get the event loop for asyncio
self.loop = HABApp.core.const.loop
# No Apple TV connected yet
self.atv = None
# Listen to control commands from openHAB (from proxy String AppleTV_Control)
self.listen_event( 'AppleTV_Control', self.control, ItemCommandEvent )
# Scan and connect
self.run_soon(self.connect)
# Poll status every 5s
self.run_every(None,5,self.status)
Please try that!
@markus7017
+1 for oh3!
Would be nice to see this binding built for Openhab 3!
Would be really nice to control the ATV through openhab!
Hi all-
Iāve spent a little time looking at what needs to be done to get this binding working on OH3 and with newer ATV devices. I spent quite a bit of time trying to get jpy working and didnāt have much success. I think the limitations placed on us by OSGI and jpy make the problem unlikely to be easily solved.
However, pyatv includes a utility called atvscript, which I think can be used to avoid java-python inter-operation.
As a first step, I managed to get discovery working. I think pairing without running atvremote on the command line is going to be hard to do, but with good documentation that might not be a huge problem. The atvscript tool can also push updates to us in real-time, so thereās actually an advantage versus polling as the current binding does.
Hopefully most of the binding code can simply be reused, which is always a plus. Not sure how much time Iāll have to work on this, but if anyone is interested in testing, let me know!
Somehow the OH runtime includes jpy, but I never got to the point to get access to this instance. In general that shouldnāt be unsolvable, but you are right I also spent a lot of time on this. The reason for polling was just a lack of Python knowledge. The bindings brings a itās own and modified copy of the older pyatv. The way to include external components in OH3 has also changed. The binding did itās job for OH2, but I think a rework if the lower layer is required.
Using the command line tools and redirecting the output should be feasible. It did this once for my WhatsApp binding running youwsapp cli and this worked pretty well (I could re-open the repo if you are interested in the code). Let me know if you have questions in the existing code.
And yes, I could support testing, because I canāt control my ATV from OH anymore
Hi-
Thanks for your feedback! I appreciate that youāve responded so quickly. I had a look through your code for the binding and it seems pretty reasonable. Generally speaking, I give my work freely to whoever wants to use it, so regardless of whether I succeed, the changes I try will be free for anyone to take.
I do like the idea of jpy, and (oddly enough) Iāve got experience with java/native interaction. Unfortunately, I think that the restrictions OH3 have put in place mean that itās maybe not worth trying. I am not a python expert but I think if (as the ultimate goal) the binding were to read the output of a python script that runs commands without having to start it each time, we can have all of the benefits of embedded python without the hassle of getting it to work. Thereās support in pyatv for doing that with events, so I think any changes needed should be possible to send back to the pyatv folks.
Long story short, I have an idea of how this might be done, but I would be glad to look at your code for inspiration! I can keep you posted and once I have something that seems like it works, Iāll push it somewhere you can have a look.
One other thing, it seems like pyatv can control other devices like HomePod minis and suchā¦ was wondering if maybe there was a better name for the binding but am not sure I have a good idea. Something maybe to think about!
Hello all-
Iāve been working on producing a version of this binding that works with OpenHAB 3 and have something that I think Iād call a āproof of concept.ā Itās very early in the development phase, so I wouldnāt recommend this for the faint of heart. That said, the basic functionality seems to work, and there are a number of additional features beyond what the previous versions provided. Iād love to get some feedback if anyone is willing to have a go.
Some details of whatās new, changed and where problems likely lurk are in the README:
https://git.sr.ht/~hww3/org.openhab.binding.appletv
Iāve also prepared a jar with the current version of the code, so the portion of the install dealing with building the binding can be skipped by downloading the pre-built jar from this download page:
https://git.sr.ht/~hww3/org.openhab.binding.appletv/refs/0.1.0
Youāll still need to install the enhanced version of pyatv, as described in the instructions. Hopefully in the future that can be simplified as well.
Note: I donāt use item or thing configuration files myself, so any questions about how to set those up (vs using auto-discovery) will probably need to be answered by others.
As always, please feel free to get in touch with any comments, questions or suggestions.
Hi there,
as iĀ“m now also a owner of an apple-tv, i just wonder what the current state is here?? IĀ“m very open to do some tests if needed. Does it make any sense to use the code from @hww3 ??
IĀ“m currently running openHAB 3.3.0.RC1 on Openhabian (raspberry) and have an ATV 4k, latest software-release. Let me know, how I can help, even IĀ“m not able to codeā¦
cheers
Hi-
Since youāre running openHAB 3, the rewrite of the binding Iām working on is your only option, as the previous version wonāt work at all. Iām actively working on it, so if you do run into problems using it, I can likely fix them pretty quickly.
Iām in the middle of adding support for using AppleTV devices as āaudio sinksā so that you can play sounds or speech to them. That will probably be ready for testing in a week or two.
Sorry for delayed answer, I missed out your answer.
back to topic: So you recommend to use the .jar in my environment, cool!
Will do so and report back, might take a while, much other things to do in summer
Regarding āAudio Sinkā: Do you think this make sense? A ATV do not have own speakers (AFAIK), so you have anything additionally switched on and run the whole time to get the output heared. In my case it is a receiver, which consumes (for me) to much power to let it run 24/7. A Alexa/HomePod etc. consumes much less power, so it is for me the much better solution. Not sure, whether I got your use case for this??
Anyhow, I do not have a concrete use case for the ATV at all at the moment, just want to āseeā it and probably switch it ON/OFF from my openhab-environment.
Yes, it may not be super helpful to use it as a default audio sink, but if there was a notification and you were actively using the ATV device (which is a condition you could check), you might want to route the audio to it.
Also, calling this the āApple TVā binding is a bit misleading, as there are a number of devices that can potentially be controlled by it. Iām looking at supporting HomePods and other devices that support AirPlay, and those could be a default audio destination. Getting that working reliably is going to take a bit of work, so itās not going to happen immediately but I think itās a nice option for the future.
Anyhow, feedback is always welcome and Iāll try to keep those links updated with the latest versions!
Hi, I just installed the most recent build with the current snapshot (3.4.0-SNAPSHOT - Build #3011). I had some trouble getting pyatv working, due to a library issue with miniaudio. For that, I had to install it from source, modifying the libs outlined here: _miniaudio.abi3.so: undefined symbol: __atomic_load_8 on raspbian Ā· Issue #52 Ā· irmen/pyminiaudio Ā· GitHub
I am only able to pair with the ācompanionā protocol, and it wont receive any updates, giving the following error:
{
"result": "failure",
"datetime": "2022-07-20T15:14:33.403169-07:00",
"exception": "power_state is not supported",
"stacktrace": "Traceback (most recent call last):\n File \"/usr/local/lib/python3.7/dist-packages/pyatv/scripts/atvscript.py\", line 452, in appstart\n print(args.output(await _handle_command(args, abort_sem, loop)), flush=True)\n File \"/usr/local/lib/python3.7/dist-packages/pyatv/scripts/atvscript.py\", line 256, in _handle_command\n return await _run_command(atv, args, abort_sem, loop)\n File \"/usr/local/lib/python3.7/dist-packages/pyatv/scripts/atvscript.py\", line 281, in _run_command\n output(True, values={\"power_state\": atv.power.power_state.name.lower()})\n File \"/usr/local/lib/python3.7/dist-packages/pyatv/support/shield.py\", line 72, in _guard_method\n return func(self, *args, **kwargs)\n File \"/usr/local/lib/python3.7/dist-packages/pyatv/core/facade.py\", line 338, in power_state\n return self.relay(\"power_state\")\n File \"/usr/local/lib/python3.7/dist-packages/pyatv/core/relayer.py\", line 91, in relay\n target, chain(self._takeover_protocol, priority or self._priorities)\n File \"/usr/local/lib/python3.7/dist-packages/pyatv/core/relayer.py\", line 114, in _find_instance\n raise exceptions.NotSupportedError(f\"{target} is not supported\")\npyatv.exceptions.NotSupportedError: power_state is not supported\n"
}