openairplay/airplay2-receiver

Direct HomePod Streaming Unresponsive

TheSpookyCat opened this issue ยท 50 comments

The current release of master is non-functional on Windows devices. No exception, no error, no requests even so much as logged to console. Going to test by going back a bunch of commits until I find out where it broke.

Update 1: I've gone all the way back to d28733e (when #4 was merged) and it's still not working. Very bizarre, likely just something to do with my setup.

Update 2: Restarted the device though I don't think that was the issue. I've tested it twice now and when I initiate the music playing from my HomePod, and then use my phone to also enable streaming to the receiver, it rejects the connection. Would love to know if someone else can repro

So rolling back past all PR/commits in the last week is still the same behaviour?

So rolling back past all PR/commits in the last week is still the same behaviour?

Yes. I've been unsuccessful getting it to work since. I've managed to get to the pairing stage, but even then it doesn't matter which OS version I'm on it just dies mid-pair and the connect fails ;/

can you try this https://github.com/glmnet/airplay2-receiver/tree/mix?

Works a charm on iOS 15 once I disable the HKPairing flag. I've upgraded to Public Beta 1 now and that's still an issue though. See #29 for further details on that.

I can only assume something you're working on that branch has altered whatever was breaking - good job.

Looks like natis97 figured out that direct HomePod streaming doesn't work: https://github.com/natis97/airplay2-receiver-sound/blame/master/README.md#L42

I can confirm that even on your mix branch glmnet that HomePod streaming to this script does not work and that appears to be the main problem.

Repro steps:

  1. Select the AirPlay icon on any device
  2. Select 'Control Other Speakers & TVs'
  3. Select any HomePod and play music through it
  4. Attempt to sync audio playback with ap2-receiver
  5. HomePod will stop playing music, error message will appear on the device controlling the HomePod, and the entire playback queue of the HomePod will disappear like it was never there.

I'm testing only from iPhone 14.5 local music (actually YouTube Music app mostly, not Apple Music and not "Controlling other devices") also HK Pairing to the home app does not work for me (I get a POST pair-add/ which yields in a 404, I'm not sure what the state of this is so far)

If PTP is active as master is, and your device "qualifies" for dictating the PTP clock (kind of being master) it does not work unless you don't announce yourself as a PTP source, I've made a comment about that somewhere. You can check if that is the issue easily by removing these:

'timingPort': 0,
'timingPeerInfo': {
'Addresses':
[
IPV4,
IPV6
],
'ID': IPV4}

I don't know what else can be making a difference.

@glmnet

Nope, still the same issue:
image

also HK Pairing to the home app does not work for me (I get a POST pair-add/ which yields in a 404, I'm not sure what the state of this is so far)

In regards to this, I get the same:

POST /pair-add Not implemented!
192.168.1.113 - - [13/Jul/2021 20:24:52] code 404, message Not Found

Seems like a portion of airplay is missing for HK to function. pair-add endpoint is unimplemented. There's pair-setup to build the pair, then adding to that pair via pair-add, which I guess involves a number of crypto operations to add a node to a group is performed.

Actually, it may be achievable. The PDF at the link you provided here https://developer.apple.com/homekit/specification/ suggests all of the M1-M6 steps are already available in our framework. But the endpoint is not added yet.

We have pair-setup, but the doc lists in table 5-15 e.g. With the relevant sections being 5.10 - 5.15

Value Description
0 PairSetup
1 PairSetupwithAuth
2 PairVerify
3 AddPairing
4 RemovePairing
5 ListPairings

without specifically mentioning POST pair-add (above tables are likely TLVs). Although I suspect this is a tip of the iceberg: there may be an enormous amount that needs implementing. ๐Ÿ™ˆ

Could you try the new branch https://github.com/openairplay/airplay2-receiver/tree/pair-add for this?

It's skeleton functionality for pair-add, pair-remove and pair-list. It might even work directly to add. ( Altho I think any sane device will do a pair-list first to see what's there. )

Read the included comments.

@LewdNeko and @glmnet do you have HK devices to test? If so, would you be willing to flesh out the skeleton methods with some read/write logic to save the pairings to a file?

Yeah, I can but we need to fix #29 for me to be able to test it at all.

OK - I just pushed what I think is a fix to the branch. Please give it a try :)

POST /pair-add
-----   Pair-Add [1/1]
[Redacted]
----- ENCRYPTED CHANNEL -----
----------------------------------------
Exception occurred during processing of request from ('192.168.1.113', 49653)
Traceback (most recent call last):
  File "C:\Program Files\Python39\lib\socketserver.py", line 316, in _handle_request_noblock
    self.process_request(request, client_address)
  File "C:\Program Files\Python39\lib\socketserver.py", line 347, in process_request
    self.finish_request(request, client_address)
  File "C:\Program Files\Python39\lib\socketserver.py", line 360, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "C:\Program Files\Python39\lib\socketserver.py", line 720, in __init__
    self.handle()
  File "C:\Program Files\Python39\lib\http\server.py", line 429, in handle
    self.handle_one_request()
  File "C:\Program Files\Python39\lib\http\server.py", line 395, in handle_one_request
    self.raw_requestline = self.rfile.readline(65537)
  File "C:\Program Files\Python39\lib\socket.py", line 704, in readinto
    return self._sock.recv_into(b)
  File "E:\Projects\Web Dev\airplay2-receiver\ap2\pairing\hap.py", line 596, in recv_into
    data = self.recv(nbytes, flags)
  File "E:\Projects\Web Dev\airplay2-receiver\ap2\pairing\hap.py", line 614, in recv
    block_length_bytes = self.socket.recv(self.LENGTH_LENGTH)
  File "E:\Projects\Web Dev\airplay2-receiver\ap2\pairing\hap.py", line 605, in recv
    assert not flags and buflen > self.LENGTH_LENGTH
AssertionError
----------------------------------------

Let me know if you need more details.

can you comment out those assert directives and see how it proceeds?
assert not flags and buflen > self.LENGTH_LENGTH

force pushed some extra comments and class Permissions

can you comment out those assert directives and see how it proceeds?
assert not flags and buflen > self.LENGTH_LENGTH

Commented out - I see a POST /pair-add, the encryption data, and then nothing. My phone shows "Unable to Add Accessory".

Note in regard to fixing #29: new error.

POST /pair-verify
-----   Pair-Verify [1/2]
----------------------------------------
Exception occurred during processing of request from ('192.168.1.113', 49719)
Traceback (most recent call last):
  File "C:\Program Files\Python39\lib\socketserver.py", line 316, in _handle_request_noblock
    self.process_request(request, client_address)
  File "C:\Program Files\Python39\lib\socketserver.py", line 347, in process_request
    self.finish_request(request, client_address)
  File "C:\Program Files\Python39\lib\socketserver.py", line 360, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "C:\Program Files\Python39\lib\socketserver.py", line 720, in __init__
    self.handle()
  File "C:\Program Files\Python39\lib\http\server.py", line 429, in handle
    self.handle_one_request()
  File "C:\Program Files\Python39\lib\http\server.py", line 415, in handle_one_request
    method()
  File "E:\Projects\Web Dev\airplay2-receiver\ap2-receiver.py", line 368, in do_POST
    self.handle_pair_verify()
  File "E:\Projects\Web Dev\airplay2-receiver\ap2-receiver.py", line 700, in handle_pair_verify
    res = self.server.hap.pair_verify(body)
  File "E:\Projects\Web Dev\airplay2-receiver\ap2\pairing\hap.py", line 152, in pair_verify
    res = self.pair_verify_m1_m2(req[Tlv8.Tag.PUBLICKEY])
  File "E:\Projects\Web Dev\airplay2-receiver\ap2\pairing\hap.py", line 486, in pair_verify_m1_m2
    accessory_signed = self.accessory_ltsk.sign(accessory_info)
AttributeError: 'Hap' object has no attribute 'accessory_ltsk'

accessory_ltsk

OK - force pushed maybe a fix - can you try to repro this error again @LewdNeko ?

Could you try the new branch https://github.com/openairplay/airplay2-receiver/tree/pair-add for this?

It's skeleton functionality for pair-add, pair-remove and pair-list. It might even work directly to add. ( Altho I think any sane device will do a pair-list first to see what's there. )

Read the included comments.

@LewdNeko and @glmnet do you have HK devices to test? If so, would you be willing to flesh out the skeleton methods with some read/write logic to save the pairings to a file?

At this point I managed to pair the receiver. iOS 14.5.
I don't understand what you need help with. I have an airport express and a HomePod mini

Didn't test your pushes after this point

Note in regard to fixing #29: new error.

AttributeError: 'Hap' object has no attribute 'accessory_ltsk'

What number is in the X-Apple-HKP header, if present, just before it stops?

Could you try the new branch https://github.com/openairplay/airplay2-receiver/tree/pair-add for this?
It's skeleton functionality for pair-add, pair-remove and pair-list. It might even work directly to add. ( Altho I think any sane device will do a pair-list first to see what's there. )
Read the included comments.
@LewdNeko and @glmnet do you have HK devices to test? If so, would you be willing to flesh out the skeleton methods with some read/write logic to save the pairings to a file?

At this point I managed to pair the receiver. iOS 14.5.
I don't understand what you need help with. I have an airport express and a HomePod mini

Didn't test your pushes after this point

Good to know. So this branch fixes something for you? Wondering whether you have any devices running HomeKit (HK) and how they behave with this.

Play music:

POST /pair-verify
-----   Pair-Verify [1/2]
----------------------------------------
Exception occurred during processing of request from ('192.168.1.113', 50125)
Traceback (most recent call last):
  File "C:\Program Files\Python39\lib\socketserver.py", line 316, in _handle_request_noblock
    self.process_request(request, client_address)
  File "C:\Program Files\Python39\lib\socketserver.py", line 347, in process_request
    self.finish_request(request, client_address)
  File "C:\Program Files\Python39\lib\socketserver.py", line 360, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "C:\Program Files\Python39\lib\socketserver.py", line 720, in __init__
    self.handle()
  File "C:\Program Files\Python39\lib\http\server.py", line 429, in handle
    self.handle_one_request()
  File "C:\Program Files\Python39\lib\http\server.py", line 415, in handle_one_request
    method()
  File "E:\Projects\Web Dev\airplay2-receiver\ap2-receiver.py", line 368, in do_POST
    self.handle_pair_verify()
  File "E:\Projects\Web Dev\airplay2-receiver\ap2-receiver.py", line 700, in handle_pair_verify
    res = self.server.hap.pair_verify(body)
  File "E:\Projects\Web Dev\airplay2-receiver\ap2\pairing\hap.py", line 154, in pair_verify
    res = self.pair_verify_m1_m2(req[Tlv8.Tag.PUBLICKEY])
  File "E:\Projects\Web Dev\airplay2-receiver\ap2\pairing\hap.py", line 488, in pair_verify_m1_m2
    accessory_signed = self.accessory_ltsk.sign(accessory_info)
AttributeError: 'int' object has no attribute 'sign'

Add to HomeKit (with hap.py#L618 commented out):

Failure at POST /pair-add still. Is pair_Add_m1_m2 missing some code?

Note in regard to fixing #29: new error.
AttributeError: 'Hap' object has no attribute 'accessory_ltsk'

What number is in the X-Apple-HKP header, if present, just before it stops?

Here's the X-Apple-X headers:
X-Apple-AbsoluteTime: 648005521
X-Apple-HKP: 6
X-Apple-Client-Name: iphon ;3
X-Apple-PD: 1

I'm assuming the failure is because we're not returning a success?

# TODO: Point 3: the accessory must return success - what flags = success?

My Discord is Neko#0013 if that makes this easier to debug - I'm completely clueless as to what's going on with hap.py

Update: I've tried adding

        self.accessory_ltsk = nacl.signing.SigningKey.generate()
        self.accessory_ltpk = bytes(self.accessory_ltsk.verify_key)

to Hap.__init__ - That 'works' now. Only issue is that afterwards I get a POST /fp-setup after which the server just hangs and the phone eventually times out the setup.

X-Apple-HKP: 6
X-Apple-Client-Name: iphon ;3
X-Apple-PD: 1

OK - in my setup it's 4. Remember, this is skeleton code, so no IDs are saved. If you nacl.signing.SigningKey.generate() then you will overwrite the key already generated in an earlier stage. Pull my latest branch (with --force)

X-Apple-HKP: 6
X-Apple-Client-Name: iphon ;3
X-Apple-PD: 1

OK - in my setup it's 4. Remember, this is skeleton code, so no IDs are saved. If you nacl.signing.SigningKey.generate() then you will overwrite the key already generated in an earlier stage. Pull my latest branch (with --force)

I've implemented the skeleton code locally. Currently, if I return a response from pair_add_m1_m2() which has more than 2 items in the list I get the following error:

Exception occurred during processing of request from ('192.168.1.113', 50395)
Traceback (most recent call last):
  File "C:\Program Files\Python39\lib\socketserver.py", line 316, in _handle_request_noblock
    self.process_request(request, client_address)
  File "C:\Program Files\Python39\lib\socketserver.py", line 347, in process_request
    self.finish_request(request, client_address)
  File "C:\Program Files\Python39\lib\socketserver.py", line 360, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "C:\Program Files\Python39\lib\socketserver.py", line 720, in __init__
    self.handle()
  File "C:\Program Files\Python39\lib\http\server.py", line 429, in handle
    self.handle_one_request()
  File "C:\Program Files\Python39\lib\http\server.py", line 415, in handle_one_request
    method()
  File "E:\Projects\Web Dev\airplay2-receiver\ap2-receiver.py", line 372, in do_POST
    self.handle_pairing('add')
  File "E:\Projects\Web Dev\airplay2-receiver\ap2-receiver.py", line 722, in handle_pairing
    res = self.server.hap.pair_add(body)
  File "E:\Projects\Web Dev\airplay2-receiver\ap2\pairing\hap.py", line 219, in pair_add
    return Tlv8.encode(res)
  File "E:\Projects\Web Dev\airplay2-receiver\ap2\pairing\hap.py", line 101, in encode
    length = len(value)
TypeError: object of type 'int' has no len()

Easily repro-able by returning something like:

                return [
                    Tlv8.Tag.STATE, PairingState.M2,
                    Tlv8.Tag.ERROR, PairingErrors.AUTHENTICATION
                ]

However, if it returns the "success state":

        return [
            Tlv8.Tag.STATE, PairingState.M2,
        ]

The phone you are pairing from will return an "Accessory Not Working" error. Is it possible that this "success state" is incorrect?

However, if it returns the "success state":

        return [
            Tlv8.Tag.STATE, PairingState.M2,
        ]

The phone you are pairing from will return an "Accessory Not Working" error. Is it possible that this "success state" is incorrect?

Possibly..... Success isn't defined anywhere that I can find it (yet).

OK - in my setup it's 4. Remember, this is skeleton code, so no IDs are saved. If you nacl.signing.SigningKey.generate() then you will overwrite the key already generated in an earlier stage. Pull my latest branch (with --force)

Not seeing any changes that might be fixing the earlier issue of .

    accessory_signed = self.accessory_ltsk.sign(accessory_info)
AttributeError: 'int' object has no attribute 'sign'

Got a link handy? systemcrash/airplay2-receiver@pair-add and openairplay/airplay2-receiver@pair-add seem to be identical and not updated with whatever you did :)

TypeError: object of type 'int' has no len()

Might need an if/else to take care of length = len(value) it's trying to get the data length. (TLV).

Not seeing any changes that might be fixing this. Got a link handy? :)

Just the pair-add branch :)

Not seeing any changes that might be fixing this. Got a link handy? :)

Just the pair-add branch :)

Yeah I'm on the pair-add branch (latest) and still no cigar with that AttributeError. Almost as if that Signing Key isn't being generated in any earlier stage for me.

You might need to stash your local changes and rebase.

accessory_signed = self.accessory_ltsk.sign(accessory_info)

AttributeError: 'int' object has no attribute 'sign'

If this is the error, then I think doing this should be correct:

        self.accessory_ltsk = nacl.signing.SigningKey.generate()
        self.accessory_ltpk = bytes(self.accessory_ltsk.verify_key)

If this is the error, then I think doing this should be correct:

Yeah, I thought so too but it just does nothing until the phone times out. Really unsure what's going on with iOS 15 and AP2 xD

Even weirder, with the current pair-add branch I don't even see so much as a POST request when trying to pair using a device running iOS 14. Even normal music streaming doesn't work from iOS 14 unless I do as glmnet said before:

If PTP is active as master is, and your device "qualifies" for dictating the PTP clock (kind of being master) it does not work unless you don't announce yourself as a PTP source, I've made a comment about that somewhere. You can check if that is the issue easily by removing these

Enabling/disabling whether this speaker is the "master" should probably also be a command-line option.

Hmm - I'll see what I can do about that PTP bit.

I think the TLV encoding should be fine it we don't init LTSK to e.g. 0.

I was so tired and defeated last night I didn't even think of trying this with the latest changes on iOS 15

With these lines commented out, as well as adding the following to pair_verify_m1_m2

        self.accessory_ltsk = nacl.signing.SigningKey.generate()
        self.accessory_ltpk = bytes(self.accessory_ltsk.verify_key)

I've managed to get streaming from iOS 15 to the ap2 receiver working almost perfectly, meaing anything related to #29 is 100% fixed provided the above two lines exist.

With these lines commented out, as well as adding the following to pair_verify_m1_m2

        self.accessory_ltsk = nacl.signing.SigningKey.generate()
        self.accessory_ltpk = bytes(self.accessory_ltsk.verify_key)

OK!

I did some digging: could it be that the statusFlags are wrong? (ap2-receiver.py#L216)

https://openairplay.github.io/airplay-spec/status_flags.html

I think that ckdo's branch-sender made a start on this, and could be what we're missing. One of the flags in that spec is DeviceWasSetupForHKAccessControl. Maybe if this flag does not get set, iOS thinks that pairing failed?

https://github.com/ckdo/airplay2-receiver/tree/branch-persistentpairings works for pairing but not audio playback. Going to try salvage what I can from it and include in a PR.

https://github.com/ckdo/airplay2-receiver/tree/branch-persistentpairings works for pairing but not audio playback. Going to try salvage what I can from it and include in a PR.

It's funny you should want to have a go at that. I just spent the last hour or so fixing exactly this in HAP! See latest commit ;)

Weird how we're thinking the same thing ๐Ÿ˜„

Edit: ( non-transient pairing, which might not be the same as persistent pairing )

I'll rebase the pair-add branch onto this latest fix, if you think it helps. ( Will mean you have to rebase your branch, but... progress ).

Apologies - just force-pushed to master (to clarify last commit).

Apologies for my premature close - this issue is not yet fixed per-se.

After doing as you recommended systemcrash, I found that when selecting the speaker in HomeKit, it had solved an issue I didn't expect it to, one that I was going to raise in a separate thread. HomeKit now correctly displays the currently playing track whilst streaming to the speaker.
However, we are still not currently at a stage where a HomePod can stream to this receiver.

I have tested with and without the newly added -npm flag to no avail. I can only think of two reasons why this may be the case:

  • The current default feature set is incorrect.
  • PTP needs to be wholly implemented.

What is the status of #35? What needs doing?

* The current default feature set is incorrect.

* PTP needs to be wholly implemented.

What is the status of #35? What needs doing?

You could try turning off PTP and/or transient pairing:
-ftxor 48 41

When I disable PTP, I get a shk error for audio type 96... so:

-ftxor 48

* The current default feature set is incorrect.

* PTP needs to be wholly implemented.

What is the status of #35? What needs doing?

You could try turning off PTP and/or transient pairing:
-ftxor 48 41

When I disable PTP, I get a shk error for audio type 96... so:

-ftxor 48

For me, I get:

Traceback (most recent call last):
  File "C:\Program Files\Python39\lib\socketserver.py", line 316, in _handle_request_noblock
    self.process_request(request, client_address)
  File "C:\Program Files\Python39\lib\socketserver.py", line 347, in process_request
    self.finish_request(request, client_address)
  File "C:\Program Files\Python39\lib\socketserver.py", line 360, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "C:\Program Files\Python39\lib\socketserver.py", line 720, in __init__
    self.handle()
  File "C:\Program Files\Python39\lib\http\server.py", line 429, in handle
    self.handle_one_request()
  File "C:\Program Files\Python39\lib\http\server.py", line 415, in handle_one_request
    method()
  File "E:\Projects\Web Dev\airplay2-receiver-lewdneko\ap2-receiver.py", line 379, in do_POST
    self.handle_pair_setup()
  File "E:\Projects\Web Dev\airplay2-receiver-lewdneko\ap2-receiver.py", line 735, in handle_pair_setup
    res = self.server.hap.pair_setup(body)
  File "E:\Projects\Web Dev\airplay2-receiver-lewdneko\ap2\pairing\hap.py", line 209, in pair_setup
    res = self.pair_setup_m5_m6(req[Tlv8.Tag.ENCRYPTEDDATA])
  File "E:\Projects\Web Dev\airplay2-receiver-lewdneko\ap2\pairing\hap.py", line 381, in pair_setup_m5_m6
    self.pair_setup_m5_m6_2(dec_tlv)
  File "E:\Projects\Web Dev\airplay2-receiver-lewdneko\ap2\pairing\hap.py", line 414, in pair_setup_m5_m6_2
    with open("./pairings/" + self.device_id.decode("utf-8") + ".pub", "wb") as device_pairing_file:
OSError: [Errno 22] Invalid argument: './pairings/D4:F4:6F:06:49:82.pub'

Why are we trying to write a pairing at this point? This must've been something I missed when merging ckdo's branch into my own.

I think I fixed all the most recent errors here. But not sure about Streaming. I think this may be a product if how HAP should work. The spec mandates up to 8 connections. Whereas AP2 in this module supports ONE. So if another iDevice is online with this receiver, the HomePod might just crap out.

So... in my dev branch there are some developments. I opened Pandora's box: multi-threading.

@LewdNeko if you can test from my dev branch, and see how that works for you and your HP, I'd appreciate it.

So... in my dev branch there are some developments. I opened Pandora's box: multi-threading.

@LewdNeko if you can test from my dev branch, and see how that works for you and your HP, I'd appreciate it.

You my friend are pure magic. Streaming audio to the receiver & any amount of extra compatible devices works regardless of which device starts receiving first.
I did encounter a whole host of issues but these are all present with the master branch too so I would say your implementation for Ft59 is working flawlessly. Great job! ๐Ÿฅ‡