Legacy Yamaha Receiver Control - Use older Yamaha receivers with Home Assistant!

I recently inherited my family’s 2007-era Yamaha RX-V3900 receiver. It is no slouch, featuring three zones, 1080p video, a host of analog and digital inputs and outputs, as well as 7.2 surround sound.

Serious stuff.

However, one area where the receiver was seriously lacking was the automation department. It is a creature of its time, and so is unsupported by the official Yamaha integration, as well as the other existing community offerings.

While the unit does feature an RS-232 port, it appears to be disabled, and I wasn’t able to conjure any life out of it. While of course I could have used an IR blaster, I decided to rummage around in the receiver’s internal features to see if there was anything more elegant I could use through Home Assistant.

I then discovered this delightful gem:

This is a very basic web control interface offered by the receiver. It turns out, if you connect your receiver to ethernet, then navigate to its IP address, you are served with a quaint, Javascript-powered interface. Neat!

After some digging around in the network tab of my browser, I discovered that this interface uses fairly straightforward XML packets to send commands to the receiver. This meant it would be trivial (if time consuming) to reverse-engineer those commands, and then send my own.

After working out how the various functions were used and completely reverse-engineering the protocol, I then proceeded to sit on the project for several months. After my wife asked whether it was possible to have the receiver turn on automatically when she wanted to start playing music, I promptly leapt into action and finished it off.

Installing

The integration can be installed with HACS - you just need to add it as a custom repository:

Once installed, you can add it as an integration as normal. It has preliminary support for discovering receivers over the network using SSDP, but if that fails you can provide the IP address directly.

Features

This library exposes a separate media player entity for each zone operated by the receiver. Each entity can:

  • Be turned on and off
  • Be muted, and have its volume adjusted
  • Select a source from the available list of sources (which should be populated according to the sources actually available on the receiver)
  • (In the case of the main zone) select an audio program

Currently I can only confirm that the integration works with the RX-V3900, but there is no reason why it shouldn’t work with other Yamaha receivers of the same vintage. I would really like to expand its supported model list, but obviously that means I will need as many users as possible to test it on their hardware (see further below).

This integration uses a separate Python library I created, which also allows for some more low-level interaction with the receiver.

What’s next / How can I help?

I’m so glad you asked.

I would like to test this integration with as many Yamaha receivers as possible. Later Yamaha receiver models largely seem to have gone in different directions, supporting protocols like YNCA or even the official integration. However, several other Yamaha receivers from this era also seem to have used this web control interface.

If you have a receiver that uses this interface, and you would like to help me ensure this integration is compatible with it, I would be immensely grateful if you could provide me with some of your XML logs so that I can see how the interface works with different models.

To facilitate this, I have put together this library. The library contains two functions: first, a function which sets up the receiver as normal and then captures the XML packets (called ‘query’ mode); and second, a function which intercepts the XML packets between the computer and the web browser interface (called ‘relay’ mode). Both will be very useful to expanding the integration’s compatibility.

One of the reasons I need the XML logs themselves is that a great deal of the functionality seems to be hardcoded into the Javascript web control interface. For example, the interface just seems to assume that certain features exist in the receiver (such as the number of zones or the list of inputs) and then queries them, meaning that I don’t have any ability to preempt what functions a receiver might have. I also don’t know the interface was expanded for later models, or if they use the same protocol as for earlier models.

More data is the answer - so please, if you’re willing to help, dump the logs and get in touch!

Issues

If you have any issues, please raise an issue on GitHub, or post here. I am not a professional programmer by any means, and I am sure there is much that can be improved in my code.

At some stage I want to get the integration included in the official HACS repository, but that can only happen after more testing, so please give it a try and let me know how you go!

1 Like

Why would you create this when the built in yamaha network reciever integration supports this device.

it uses rxv python as the upstream library which does support the rx-v3900.

I certainly did try the built in integration - as far as I can tell, it’s not compatible with my receiver, and no matter what I try it doesn’t show up in HA. It certainly could be a case of user error!

I wasn’t aware of the other library you linked - it certainly seems like it uses the same protocol. I’ll take a look!

There are 2 yamaha integrations, you likely used the wrong one.

Yamaha Network Recievers (works with your device)
Yamaha Musiccast (Does not work with yours)

Yes, that’s what the integration I linked uses. So you definitely used the wrong integration.

Okay. I have determined the issue.

I was using the correct Yamaha integration. However, even when I specify the IP address (and it showed up as an integration in Home Assistant), it did not instantiate any media player entities. This is what led me to create the integration in the first place.

Having dug into the rxv library’s code (as well as the Yamaha Home Assistant integration), I made two observations:

  1. The rxv library does not automatically discover the RX-V3900 receiver. However, if I manually specify the IP address and the model name, it does connect.
  2. However, even when it does connect, the rxv library does not populate a list of zones for the receiver, and can’t list any of the receiver’s features. However, if I manually provide it with commands, it does correctly send them to the receiver.

The second issue is caused by the discover_features function in the rxv library. That function requests a desc.xml file from the receiver, which it uses to populates the zone information and commands that the receiver can accept. For my receiver, however, the file looks like this:

I don’t know what other receiver models’ xml files look like, but clearly this doesn’t include the information (e.g. the list of zones) that the rxv library is expecting. It therefore can’t instantiate any zones (or any other receiver features) in the normal way.

This is perhaps the direct answer to my question about how the Web Control Interface can determine what functions each receiver has - in my receiver’s iteration of the Interface, it was all hard-coded into the javascript. However in later versions, they seem to have started to use the desc.xml file.

The Yamaha Home Assistant integration uses the rxv library’s discover_features function to pull information about the receiver’s zones, which it then uses to instantiate the media_player entities. Because the rxv library can’t see that my receiver has any zones, the Home Assistant integration therefore doesn’t set any of those zones up as media entities.

This also fails silently: while the rxv library puts out a warning if it can’t see any desc.xml file, it can actually see one in my receiver’s case. It just doesn’t contain any information that allows the library to do anything useful.

I also noticed a related issue on the Home Assistant end: the “discovery” function in the Yamaha integration passes the config_info.model variable to the rxv library. However, that variable is never populated unless it gets the information from the rxv discovery function (as it doesn’t pull the value from the configuration.yaml). As that function fails, it therefore can’t use the model name to manually instantiate the rxv library object.

This is currently a moot point, as my receiver cannot be instantiated using the rxv library’s xml process. The only solution would be to effectively hard-wire the results that the rxv library would expect to see for each zone / command / etc of my receiver. That would be the equivalent of re-creating my library.

I am convinced I am not the first person who has come across this issue. For example, this user reported a similar issue with the RX-V2065 receiver, which is of a similar vintage to the RX-V3900 (see the rather confusing timeline of Yamaha receiver numbering here). That receiver seems to report no desc.xml file at all, so at least they got a warning that that was the issue.

I am grateful you directed my attention to the Yamaha integration and the rxv library, but I am also convinced that a bespoke solution is required for my receiver. I will certainly look into what would be required to adapt the rxv library to my receiver’s use case, however in the mean time if any users find that they can’t use the built-in Yamaha integration with older receivers, they can use my integration with HACS.

1 Like

Okay, so I have made some further observations.

First, I tried to work out why the rxv library’s discovery function wasn’t working. After staring into the abyss of Wireshark packet captures for a while, it seems that my receiver doesn’t follow the normal process of responding to m_search packets.

From my understanding, SSDP discovery is supposed to work by sending an m_search packet to the reserved multicast address (239.255.255.250), which all SSDP / UPNP devices listen to. Those devices then respond to the address that issued the m_search packet with a notify packet sending out information about their capabilities, etc.

For some reason, my receiver doesn’t do that - instead, every 30 seconds (or so), it sends out a notify packet to the mutlicast address advertising its capabilities. However, the rxv library isn’t designed to pick these up, so it can’t discover the receiver. This is an odd result, given that Home Assistant’s own SSDP library can in fact receive those packets, and so can see the receiver with no issue!

Second, I started to look into how the rxv library interacts with the receiver. To be frank, while some of the processes are superficially similar (e.g. they both communicate using XML, and some XML packets trigger behaviour on my receiver), the actual substance of the library is totally different. Not only is the process of discovering the receiver’s functions bound up in the desc.xml file (which my receiver does not have), the whole way that the receiver reports the status of each zone also seems completely different.

The task of trying to get the rxv library to “talk” the language of my receiver would be sizeable, and would require substantially replicating my library as a parallel protocol in rxv (for legacy receivers not compatible with its normal protocol). I’m not satisfied there is any compelling reason to do that, as I think my receiver is pretty niche at this point!

I am more than happy to be convinced otherwise, but as it stands I think the few users to which this would be of benefit would be better served by me improving my library and custom integration.

I’m not here to do that. You’re talking about an 19 year old receiver - it’s old enough to vote at this point!
Personally, I would cut my losses & realise that the juice isn’t worth the squeeze. Even if you did manage to crack it & get the receiver integrated, how many more years months do you think you’ll get out of it before its components start dying out?