neutmute / swimbait

ASP.NET Core app to emulate a Yamaha MusicCast Speaker
41 stars 4 forks source link

Good stuff #1

Open rmarinho opened 7 years ago

rmarinho commented 7 years ago

Hello there, i just wanted to chat a bit. I have a Yamaha 1600 i m trying to hack too :)

My idea is a different i m trying to make a module for HomeBridge to work with HomeKit.

I wonder if we can make this use some more shareable models so can reuse as .NET client sdk for talking with Yamaha devices.

neutmute commented 7 years ago

Hey Rui - what does the homebridge/home kit do?

You may have seen that it's an ASP.NET Core RC-1 app. Trying to emulate one of the MusicCast speakers. I got as far as having my Yamaha mobile app discover and add the fake server to its list of speakers. The next step was to receive a stream of music - but I got busy at work and progress stalled.

Anyway - I'm open to changes to factor out any code into a separate project if that helps your work. cheers!

rmarinho commented 7 years ago

Yeah what i m trying to do is different.. so home bridge allows you to fake HomeKit devices using the Apple api, a nodejs server app that you can write plugins for it. My end is to write a plugin that will allow to Yamaha to be discover to HomeKit, so you can control lights and music at the same time. Is the opposite as i talk with my own yamaha columns rest api and send commands to it.

So my plan is 1st write a server in .net core that takes commands and talks to my yamaha, then i will will create a nodejs plugin to homebridge to add the fake api for HomeKit .. well that's the plan anyway.

One thing we might share is the Models i haven't looked at the all project. will start hacking.. and see..

masto182 commented 7 years ago

Hi , I hope it's ok to comment here. I'd like to also add that this is really promising. I have a profound dislike of sonos so I invested in the musiccast system.

My only two issues with musiccast is;

I'm not a programmer so I'm not sure if the work you are doing here could solve for these cases, but either way it's great to see work that would make the musiccast system even more usable.

neutmute commented 7 years ago

Hi @masto182 - No problems - thanks for dropping by!

I think (guessing here) that the airplay restriction is from Apple since multiroom works with Spotify there shouldn't be a technical limitation in MusicCast.

Never heard of MaryTTS - that looks quite interesting. This project isn't about transmitting to the speakers but emulating a speaker. It might not be too hard though once the emulation is done. Integrating with MaryTTS sounds fun though. I could send messages to the kids downstairs without yelling :)

neutmute commented 7 years ago

@rmarinho right - got you. very cool! Will you be talking to your Yamaha over musiccast?

rmarinho commented 7 years ago

@neutmute yes i guess it's the only public api on the Yamaha right, or airplay?

Seems there's a older api https://github.com/PSeitz/yamaha-nodejs but that doesn't work with my speakers after looking at the code seems it talks with a old api that Yamaha had in the past?!

https://github.com/PSeitz/yamaha-nodejs/blob/master/availableCommands.txt

neutmute commented 7 years ago

right - I never found any docs re: this or any Yamaha protocol - it's all been reverse engineered using wireshark. The UDP was the hardest to work out buried amongst all the traffic. You shouldn't have to worry about that - but yes the REST API should have some common points that you can use and we can collab on.

rmarinho commented 7 years ago

Yap, i used wireshark, discover the endpoint, when to see someone on github calling that api (Same endpoint name) and found you :)

I will add a extra project called client, that will call the api, we could use that in the future to test your server implementation too!

rmarinho commented 7 years ago

It's running on the mac

roject Common (.NETStandard,Version=v1.6) was previously compiled. Skipping compilation. Project server (.NETCoreApp,Version=v1.0) was previously compiled. Skipping compilation. Started the server. Listing on http://192.168.1.1:80;http://192.168.1.1:51123;http://192.168.1.1:51100 Press 'Q' to stop the server UDP-Socket setup done...

M-Search sent...

HTTP/1.1 200 OK HOST: 239.255.255.250:1900 EXT: CACHE-CONTROL: max-age=100 LOCATION: http://192.168.1.6:80/description.xml SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.15.0 hue-bridgeid: 001788FFFE29FEED ST: upnp:rootdevice USN: uuid:2f402f80-da50-11e1-9b23-00178829feed::upnp:rootdevice

HTTP/1.1 200 OK HOST: 239.255.255.250:1900 EXT: CACHE-CONTROL: max-age=100 LOCATION: http://192.168.1.6:80/description.xml SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.15.0 hue-bridgeid: 001788FFFE29FEED ST: uuid:2f402f80-da50-11e1-9b23-00178829feed USN: uuid:2f402f80-da50-11e1-9b23-00178829feed

HTTP/1.1 200 OK HOST: 239.255.255.250:1900 EXT: CACHE-CONTROL: max-age=100 LOCATION: http://192.168.1.6:80/description.xml SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.15.0 hue-bridgeid: 001788FFFE29FEED ST: urn:schemas-upnp-org:device:basic:1 USN: uuid:2f402f80-da50-11e1-9b23-00178829feed

HTTP/1.1 200 OK Location: http://192.168.1.7:49154/MediaRenderer/desc.xml Cache-Control: max-age=1800 Content-Length: 0 Server: Linux/3.2 UPnP/1.0 Network_Module/1.0 (YSP-1600) EXT: ST: urn:schemas-upnp-org:device:MediaRenderer:1 USN: uuid:9ab0c000-f668-11de-9976-00a0decf3a07::urn:schemas-upnp-org:device:MediaRenderer:1 X-ModelName: YSP-1600:00A0DECF3A07:iSound

HTTP/1.1 200 OK HOST: 239.255.255.250:1900 EXT: CACHE-CONTROL: max-age=100 LOCATION: http://192.168.1.6:80/description.xml SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.15.0 hue-bridgeid: 001788FFFE29FEED ST: upnp:rootdevice USN: uuid:2f402f80-da50-11e1-9b23-00178829feed::upnp:rootdevice

HTTP/1.1 200 OK HOST: 239.255.255.250:1900 EXT: CACHE-CONTROL: max-age=100 LOCATION: http://192.168.1.6:80/description.xml SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.15.0 hue-bridgeid: 001788FFFE29FEED ST: uuid:2f402f80-da50-11e1-9b23-00178829feed USN: uuid:2f402f80-da50-11e1-9b23-00178829feed

HTTP/1.1 200 OK HOST: 239.255.255.250:1900 EXT: CACHE-CONTROL: max-age=100 LOCATION: http://192.168.1.6:80/description.xml SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.15.0 hue-bridgeid: 001788FFFE29FEED ST: urn:schemas-upnp-org:device:basic:1 USN: uuid:2f402f80-da50-11e1-9b23-00178829feed

neutmute commented 7 years ago

That's awesome!

neutmute commented 7 years ago

So you have probably discovered the KeyHandler_KeyEvent but if you randomly punch through some of those keys while the Android/Ios MusicCast app is trying to connect with a new MusicCast device then you should see a bunch of activity in the console and then the app sees the fake server and lists it in its music cast speaker list.

Then you will have seen everything the project does. It doesn't accept a music stream yet :) Any work you do for the client should dovetail nicely in though!

rmarinho commented 7 years ago

Can't get it to connect,, open the iOS app, set to discover, press C, crashed , that's the one to connect right ? M works, it prints what it finds, it's finding my Yamaha so it's working .

Unhandled Exception: System.Net.Sockets.SocketException: Permission denied

rmarinho commented 7 years ago

ok, so it was crashing because if the ip. here https://github.com/neutmute/swimbait/blob/master/src/server/Services/MulticastService.cs#L40

changed to the ip of my phone right?

Did you got it to work with this code for you?

rmarinho commented 7 years ago

There's a bunch of iP s in different parts of the code, we need to centralize that in a config file.

It's not crashing but i can't connect, so, do we still need a real Yamaha speakers to run the server? seems you have to relay that call to a real speaker and then back?

neutmute commented 7 years ago

Agree need to fix those IP hardcodes. That particular ip on line 40 is the subnet broadcast address. I'll do some work to try and fix those parts up tonight...and check which key is for connect - it may have been two toggling at rapid speed as i recall. will document! At work now - fyi my timezone is GMT+10

neutmute commented 7 years ago

re: real speaker relay - yes there is some encrypted comms - which i mount a man in the middle on. if its an encrypted (secure route IIRC) request then I relay to a real MusicCast and then replay it back to the caller.

rmarinho commented 7 years ago

Ohh, what's encrypted ? Btw got client started, can set it on and off :) #3

rmarinho commented 7 years ago

Yeah i m going to bed now 2 AM here.. :)

WaliRedshoe commented 7 years ago

Hi, My name is Walter I found your blog and project based on the search "/YamahaExtendedControl/v1/system/" 7 days ago. Now I see some recent activities :-). Maybe I can profit from your work. My idea is, to create a central device (raspberry pi with touch screen) for controlling and monitoring a multi room system. I'm still in the evaluate process. The possible systems are:

One of the criterias is, to have a documented API (like Heos) or a chance to reverse engineer it. I already asked at Yamaha if they plan to document their API. The answer I got was: "An API for the MuiscCast system has not been published at the time by our Japanese developers.

For_ individual devices, the network interface description is published. These are available only on request."

The network interface is a TCP based protocoll with maximal one concurrent session.

If I buy a Yamaha System, I will come back and share my learnings.

rmarinho commented 7 years ago

Nice!

neutmute commented 7 years ago

Hi Walter - thanks for dropping by. I don't own a Sonos but I've seen some at a friends. I didn't know about Heos until after I bought my MusicCast enabled Yamaha amp and don't know much about it.

I'm quite liking the MusicCast system though. I find it reasonably open/easy to hack on. As mentioned in this thread there are some encrypted comms when the MusicCast app is talking to the server. The app holds Pandora/Spotify credentials so I'm assuming that is what is getting passed though. The Secure Controller is here and currently does a man in the middle.

Aside from looking behind the curtain, as a user I really enjoy the system. It feels like Yamaha could do very well if they open MusicCast to an ecosystem of third party devices. Sonos has a bit of a proprietary feel. Not sure if that is true in reality or not.

So you don't own any musiccast hardware yet? Maybe you should buy all three systems to evaluate :) Do you think Yamaha would give us the upon-request-interface-description? That might be handy.

WaliRedshoe commented 7 years ago

As I mentioned, the network interface is a TCP raw socket and RS-232 interface, not http. It is used to controll AV receivers. I have found an older version on the net. See the attached file. Take a look into it. Yamaha-YNCA-Receivers.pdf. Is there any similarity to the functionality you revers engineered?

Thanks for the man in the middle code. It will help if I have to do more revers engineering. But first I have to buy the system ;-)

neutmute commented 7 years ago

OK i see. Not so useful then.

NovaGL commented 7 years ago

If anyone is interested I have worked with the guy behind Home Remote and have got MusicCast talking to Cortana and it supports almost all the commands besides initial pairing.

If you want some help with JSON responces let me know

@WaliRedshoe You are mistaken. The Yamaha devices before MusicCast use XPATH the full API can be found here http://homematic-forum.de/forum/download/file.php?id=27557&sid=065591f1b3125e66ded21e163f9b38e1

The MusicCast devices use JSON it is much simpler to send commands and most things are unsecured and easy to access.

neutmute commented 7 years ago

Hey @NovaGL - I'm sure any knowledge you have to share will be welcome!

Something I haven't discovered yet is how to throw music to a MusicCast speaker. Investigating the NetUSB and Server sources shows that the speaker itself is very much in control of the source and the remote is just iterating through a menu structure to select a music stream that the speaker exposes as an option.

The This Phone source could be promising since the music is coming from the remote device and needs to transmit it to the speaker. Do you have any knowledge of that area?

Agreed the JSON protocol is reasonably simple and straight forward to deduce what is going on. There is also some UDP traffic being sent around the network by the MusicCast system. Some of it is for device discovery which I have emulated. Some appears to be some sort of heartbeat or perhaps a timing beacon for keeping linked speakers in sync (my guess). Do you have anything on that?

NovaGL commented 7 years ago

You can throw music to the MusicCast via DLNA will have to see if it can be done programatically as I haven't really tried.

UPnP is mostly ignored so commands like volume do not work.

The "This Phone" source does transmit over DLNA\UPnP so that does seem the most logical. They also have private areas of the UPnP tree it seems.

I only have one soundbar so can't do anything like that. But bluetooth is 100% controlled over JSON.

I set mine to automatically connect to my bluetooth speaker and transmit using my app.

I have just set mine to auto update every 1000ms a bit messy but it does the job.

For lists I just look at the max_line json result and implement a variable that increments by 8 till max line.

For Pandora besides account information, everything can be controlled including station creation, I almost was able to do custom search and creation but I think I was missing a step.

Let me know if you have any other questions. For testing I use arc welder for emulating on the PC and fiddler or Charles which I have found quite good.

Here is an example using the my phone area in device spy

<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/">
    <item id="1" parentID="0" restricted="1">
        <dc:title>People Help the People (Live at Sydney Opera House, April 2013)</dc:title>
        <upnp:artist>Birdy</upnp:artist>
        <upnp:album>Live From Sydney Opera House - EP</upnp:album>
        <upnp:class>object.item</upnp:class>
        <res protocolInfo="http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=00;DLNA.ORG_CI=1;DLNA.ORG_FLAGS=01700000000000000000000000000000" sampleFrequency="44100" bitsPerSample="16" nrAudioChannels="2">http://192.168.1.21:50163/audio/360b3cad755c45bb</res>
        <upnp:albumArtURI>http://192.168.1.21:50163/image/360b3cad755c45bb</upnp:albumArtURI>
    </item>
</DIDL-Lite>