postlund / pyatv

A client library for Apple TV and AirPlay devices
https://pyatv.dev
MIT License
893 stars 99 forks source link

Unable to use with SONOS (Ikea SYMFONISK): Authentication error: not authenticated #1259

Closed jnnkB closed 1 year ago

jnnkB commented 3 years ago

Describe the bug

I'm unable to stream any audio to my SONOS. This is what I tried:

$ atvremote scan
Scan Results
========================================
$ atvremote -s <IP ADDRESS OF SONOS> scan
Scan Results
========================================
       Name: <AIRPLAY NAME OF SONOS>
   Model/SW: Unknown Model Unknown OS
    Address: <IP ADDRESS OF SONOS>
        MAC: <MAC ADDRESS OF SONOS>
 Deep Sleep: False
Identifiers:
 - <MAC ADDRESS OF SONOS>
 - <MAC ADDRESS OF SONOS WITHOUT COLONS>
Services:
 - Protocol: AirPlay, Port: 7000, Credentials: None
 - Protocol: RAOP, Port: 7000, Credentials: None

$ atvremote -s <IP ADDRESS OF SONOS> --id <MAC ADDRESS OF SONOS WITHOUT COLONS> features
Feature list:
-------------
VolumeUp: Available
VolumeDown: Available
Title: Unavailable
Artist: Unavailable
Album: Unavailable
TotalTime: Unavailable
Position: Unavailable
PushUpdates: Available
PlayUrl: Unavailable
StreamFile: Available
Volume: Available
SetVolume: Available

Legend:
-------
Available: Supported by device and usable now
Unavailable: Supported by device but not usable now
Unknown: Supported by the device but availability not known
Unsupported: Not supported by this device (or by pyatv)
$ atvremote -s <IP ADDRESS OF SONOS> --id <MAC ADDRESS OF SONOS WITHOUT COLONS> stream_file=audio.mp3
2021-08-22 20:32:23 ERROR [root]: Authentication error: not authenticated
Traceback (most recent call last):
  File "~/.env/lib/python3.8/site-packages/pyatv/scripts/atvremote.py", line 690, in _exec_command
    value = await tmp(*args)
  File "~/.env/lib/python3.8/site-packages/pyatv/support/facade.py", line 333, in stream_file
    await self.relay("stream_file")(file, **kwargs)
  File "~/.env/lib/python3.8/site-packages/pyatv/raop/__init__.py", line 296, in stream_file
    await client.initialize(self.service.properties)
  File "~/.env/lib/python3.8/site-packages/pyatv/raop/raop.py", line 397, in initialize
    await self._setup_session()
  File "~/.env/lib/python3.8/site-packages/pyatv/raop/raop.py", line 424, in _setup_session
    resp = await self.rtsp.setup(self.control_client.port, self.timing_client.port)
  File "~/.env/lib/python3.8/site-packages/pyatv/raop/rtsp.py", line 209, in setup
    return await self.exchange(
  File "~/.env/lib/python3.8/site-packages/pyatv/raop/rtsp.py", line 302, in exchange
    resp = await self.connection.send_and_receive(
  File "~/.env/lib/python3.8/site-packages/pyatv/support/http.py", line 381, in send_and_receive
    raise exceptions.AuthenticationError("not authenticated")
pyatv.exceptions.AuthenticationError: not authenticated
$ atvremote -s <IP ADDRESS OF SONOS> --id <MAC ADDRESS OF SONOS> stream_file=audio.mp3                  1704ms  20:32:24
2021-08-22 20:32:41 ERROR [root]: Authentication error: not authenticated
Traceback (most recent call last):
  File "~/.env/lib/python3.8/site-packages/pyatv/scripts/atvremote.py", line 690, in _exec_command
    value = await tmp(*args)
  File "~/.env/lib/python3.8/site-packages/pyatv/support/facade.py", line 333, in stream_file
    await self.relay("stream_file")(file, **kwargs)
  File "~/.env/lib/python3.8/site-packages/pyatv/raop/__init__.py", line 296, in stream_file
    await client.initialize(self.service.properties)
  File "~/.env/lib/python3.8/site-packages/pyatv/raop/raop.py", line 397, in initialize
    await self._setup_session()
  File "~/.env/lib/python3.8/site-packages/pyatv/raop/raop.py", line 424, in _setup_session
    resp = await self.rtsp.setup(self.control_client.port, self.timing_client.port)
  File "~/.env/lib/python3.8/site-packages/pyatv/raop/rtsp.py", line 209, in setup
    return await self.exchange(
  File "~/.env/lib/python3.8/site-packages/pyatv/raop/rtsp.py", line 302, in exchange
    resp = await self.connection.send_and_receive(
  File "~/.env/lib/python3.8/site-packages/pyatv/support/http.py", line 381, in send_and_receive
    raise exceptions.AuthenticationError("not authenticated")
pyatv.exceptions.AuthenticationError: not authenticated
$ atvremote -s <IP ADDRESS OF SONOS> --id <MAC ADDRESS OF SONOS> play_url=https://my.test.domain/audio.mp3
2021-08-22 20:33:32 ERROR [root]: Authentication error: status code: 404
Traceback (most recent call last):
  File "~/.env/lib/python3.8/site-packages/pyatv/scripts/atvremote.py", line 690, in _exec_command
    value = await tmp(*args)
  File "~/.env/lib/python3.8/site-packages/pyatv/support/facade.py", line 326, in play_url
    await self.relay("play_url")(url, **kwargs)
  File "~/.env/lib/python3.8/site-packages/pyatv/airplay/__init__.py", line 115, in play_url
    return await self._play_task
  File "~/.env/lib/python3.8/site-packages/pyatv/airplay/player.py", line 61, in play_url
    raise exceptions.AuthenticationError(f"status code: {resp.code}")
pyatv.exceptions.AuthenticationError: status code: 404
$ atvremote -s <IP ADDRESS OF SONOS> --id <MAC ADDRESS OF SONOS> set_volume=80
$ atvremote -s <IP ADDRESS OF SONOS> --id <MAC ADDRESS OF SONOS> volume
33.0
$ atvremote -s <IP ADDRESS OF SONOS> --id <MAC ADDRESS OF SONOS> cli
Enter commands and press enter
Type help for help and exit to quit
pyatv> volume
33.0
pyatv> set_volume 80
2021-08-22 20:35:00 ERROR [root]: Unknown command: set_volume 80
pyatv> volume_up
pyatv> volume
37.99999999999999
pyatv> volume_down
pyatv> volume
32.99999999999999

System Setup (please complete the following information):

Additional context

postlund commented 3 years ago

Looks like it requires authentication. Would be great if you provided the feature string of the device so I can verify that. You can get it by scanning with debug enabled and looking for the service, all properties are logged with it. An example from my HomePod:

2021-08-22 21:53:55 DEBUG [pyatv.support.scan]: Auto-discovered Office at 10.0.10.84:7000 via Protocol.AirPlay ({'acl': '0', 'btaddr': 'AA:BB:CC:DD:EE:FF', 'deviceid': 'FF:EE:DD:CC:BB:AA', 'fex': 'AMp/StBrNTw', 'features': '0x4A7FCA00,0x3C356BD0', 'flags': '0x404', 'gid': 'xxx', 'igl': '1', 'gcgl': '1', 'model': 'AudioAccessory5,1', 'protovers': '1.1', 'pi': 'xxx', 'psi': 'xxx', 'pk': 'xxx', 'srcvers': '550.10', 'osvers': '14.7', 'vv': '2'})

The value after features is what I'm interested in.

jnnkB commented 3 years ago

The value is 0x445F8A00,0x1C340

postlund commented 3 years ago

I believe it will require transient paring to work, something I'm partially done with. I still need to figure out how the encryption is supposed to work, but hopefully I can fix that in a not too distant future. Should be possible to verify with my HomePod.

jnnkB commented 3 years ago

That sounds good.

jnnkB commented 3 years ago

Hey, you seem to have worked on pyatv a lot and the release even says something about the HomePod. Should this have fixed this issue? I tried and it seems like this issue is not yet fixed.

postlund commented 3 years ago

Unfortunately no, I have not worked on this. The problem is that the AirPlay receiver in question is an AirPlay 2-only receiver, so it doesn't support the AirPlay 1 protocol which is the only one implemented in pyatv. So even if I were to make pairing and encryption work (which is the easy part), I would still have to implement the rest of the AirPlay 2 protocol as well. I have not looked into how much work that would mean, so I can't make any statements in that regards. At some point I will look into doing that, but I unfortunately have some other things with higher priority to take care of first.

jnnkB commented 3 years ago

Good to know. Thanks a lot for your work.

postlund commented 3 years ago

Can you possibly help me finding the supported features string for your receiver? It is listed when you enable debug (i.e. atvremote --debug scan) for the AirPlay service of your device. IIRC it's called features and is two hexadecimal values joined by comma, e.g. 0x12345678,0x123456. I want to see if I can distinguish version 1 and 2 devices so that I can exclude the latter ones until I support them.

jnnkB commented 3 years ago

Do you mean this: https://github.com/postlund/pyatv/issues/1259#issuecomment-903325414 ?

postlund commented 3 years ago

Ah, yea, thank you! 👍

postlund commented 3 years ago

@jnnkB Can you try https://github.com/postlund/pyatv/tree/raop_test and see what happens (raop_test branch)? I'm a bit curious of the outcome.

jnnkB commented 3 years ago

Hey, sorry for the delay. I tested it now and these are the results:

The new services now read:

 - Protocol: AirPlay, Port: 7000, Credentials: None, Requires Password: False, Password: None, Pairing: Mandatory
 - Protocol: RAOP, Port: 7000, Credentials: None, Requires Password: False, Password: None, Pairing: NotNeeded

Pairing AirPlay requires a pin and Raop fails with pyatv.exceptions.NotSupportedError: legacy pairing not supported.

Streaming without pairing first doesn't work, but it fails later and there is a different error message:

Traceback (most recent call last):
  File "python3.8/site-packages/pyatv/support/http.py", line 388, in send_and_receive
    await asyncio.wait_for(event.wait(), timeout=4)
  File "python3.8/asyncio/tasks.py", line 498, in wait_for
    raise exceptions.TimeoutError()
asyncio.exceptions.TimeoutError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "python3.8/site-packages/pyatv/scripts/atvremote.py", line 716, in _run_application
    return await cli_handler(loop)
  File "python3.8/site-packages/pyatv/scripts/atvremote.py", line 510, in cli_handler
    return await _handle_commands(args, config, loop)
  File "python3.8/site-packages/pyatv/scripts/atvremote.py", line 611, in _handle_commands
    ret = await _handle_device_command(args, cmd, atv, loop)
  File "python3.8/site-packages/pyatv/scripts/atvremote.py", line 659, in _handle_device_command
    return await _exec_command(atv.stream, cmd, True, *cmd_args)
  File "python3.8/site-packages/pyatv/scripts/atvremote.py", line 682, in _exec_command
    value = await tmp(*args)
  File "python3.8/site-packages/pyatv/core/facade.py", line 311, in stream_file
    await self.relay("stream_file")(file, **kwargs)
  File "python3.8/site-packages/pyatv/protocols/raop/__init__.py", line 321, in stream_file
    await client.initialize(self.service.properties)
  File "python3.8/site-packages/pyatv/protocols/raop/raop.py", line 454, in initialize
    await self._setup_session()
  File "python3.8/site-packages/pyatv/protocols/raop/raop.py", line 483, in _setup_session
    resp = await self.rtsp.setup(
  File "python3.8/site-packages/pyatv/support/rtsp.py", line 175, in setup
    return await self.exchange("SETUP", headers=headers, body=body)
  File "python3.8/site-packages/pyatv/support/rtsp.py", line 258, in exchange
    resp = await self.connection.send_and_receive(
  File "python3.8/site-packages/pyatv/support/http.py", line 391, in send_and_receive
    raise TimeoutError(f"no response to {method} {uri} ({protocol})") from ex
TimeoutError: no response to SETUP rtsp://<IP ADDRESS>/<10 NUMBERS I DONT KNOW> (RTSP/1.0)

Using play_url= causespyatv.exceptions.NotSupportedError: play_url is not supported.

postlund commented 3 years ago

Thank you for verifying this! 👍 It very much sounds like AirPlayb 2 is required, I at least don't know what to test anymore. I guess I will have to implement support for that at some point then.

timpihl commented 3 years ago

Behöver du åtkomst remote till en Sonos SYMFONISK högtalare för att testa mot?

postlund commented 3 years ago

Jag vet inte om jag kommer så mycket längre, men om du kan fixa det så kan jag labba lite till.

postlund commented 3 years ago

I looked at feature flags from various devices and I can't find any patterns suggesting that AirPlay 1 is not supported. It's obvious that some kind of authentication is required, but it might be more picky about it being performed correctly than for instance the AirPort Express and that's something that would be a lot easier for me to verify with real hardware.

The feature flags suggest that either CoreUtils (HAP) or MFi authentication is supported, but nothing else. So, if the partial MFi authentication I'm doing is not enough, then HAP must be used which brings in all the encryption related stuff which I would like to avoid right now. AFAIK MFi authentication has not been reversed engineered, at least not on software level, so I can't do that verification properly.

timpihl commented 3 years ago

Hur vill du komma åt högtalaren för att bäst testa?

postlund commented 3 years ago

Det ideala hade varit tillgång till SSH på en virtuell maskin med högtalaren åtkomlig från den maskinen. Jag behöver inte root men pyatv installerad en gång (så att alla systemberoenden finns på plats eftersom installation av dem kan kräva root).

agustinf commented 2 years ago

I've tried connecting to a WiiM device (ASR001) from https://wiimhome.com/ and the results are exactly the same! $ atvremote --debug scan

2022-08-14 13:37:43 DEBUG [pyatv.core.scan]: Auto-discovered Living Room at 192.168.1.133:7000 via Protocol.AirPlay ({'acl': '0', 'deviceid': '0A:E9:F6:90:01:9E', 'features': '0x445F8A00,0x1C340', 'rsf': '0x0', 'fv': 'p20.4.6.426049', 'flags': '0x4', 'model': 'Hi-Res Audio Streamer', 'manufacturer': 'Linkplay Technology Inc.', 'serialnumber': 'FF9700162685CA053A40212A', 'protovers': '1.1', 'srcvers': '366.0', 'pi': '0A:E9:F6:90:01:9E', 'gid': '353614aa-18b9-4dd1-8592-7da5cfe1cb0a', 'gcgl': '0', 'pk': '502da4d3203ee0a5d86cd0d919d892045bee7156cec3a85d416c0f0d06ef935f'})
2022-08-14 13:37:43 DEBUG [pyatv.core.scan]: Auto-discovered 0AE9F690019E@Living Room at 192.168.1.133:7000 via Protocol.RAOP ({'cn': '0,1', 'da': 'true', 'et': '0,4', 'ft': '0x445F8A00,0x1C340', 'fv': 'p20.4.6.426049', 'md': '0,1,2', 'am': 'Hi-Res Audio Streamer', 'sf': '0x4', 'tp': 'UDP', 'vn': '65537', 'vs': '366.0', 'pk': '502da4d3203ee0a5d86cd0d919d892045bee7156cec3a85d416c0f0d06ef935f'})

$ atvremote --id [MAC OF WiiM] stream_file=https://xxx.duckdns.org:8123/api/tts_proxy/google_translate.mp3

2022-08-14 17:04:36 ERROR [pyatv.scripts.atvremote]: Authentication error: not authenticated

$ atvremote -s <IP ADDRESS OF WiiM> --id <MAC ADDRESS OF WiiM> set_volume=80 $ atvremote -s <IP ADDRESS OF WiiM> --id <MAC ADDRESS OF WiiM> volume

33.0

$ atvremote -s 192.168.1.xxx --id [mac-address] cli

pyatv> volume
33.0
pyatv> volume_up
pyatv> volume
37.99999999999999
pyatv> volume_down
pyatv> volume
32.99999999999999
postlund commented 1 year ago

Anyone with a SONOS device that can re-test this? It should work now.

postlund commented 1 year ago

I believe this works (since I know sonos works) and will close the issue. Please re-open if still a problem.