Legrand Nuvo multi-room audio support

Got some pattern matching figured out. Also I get a <CR><LF> at the end of ever response (original code only has <CR>. On to the next bit.

2019-11-03 00:44:37 INFO (SyncWorker_18) [pynuvo] Sending "Z01STATUS?"
2019-11-03 00:44:37 DEBUG (SyncWorker_18) [pynuvo] Expecting response from command sent - Data received but no EOL yet :(
2019-11-03 00:44:37 DEBUG (SyncWorker_18) [pynuvo] Expecting response from command sent - Data received but no EOL yet :(
2019-11-03 00:44:37 DEBUG (SyncWorker_18) [pynuvo] Expecting response from command sent - Data received but no EOL yet :(
2019-11-03 00:44:37 DEBUG (SyncWorker_18) [pynuvo] Expecting response from command sent - Data received but no EOL yet :(
2019-11-03 00:44:37 DEBUG (SyncWorker_18) [pynuvo] Expecting response from command sent - Data received but no EOL yet :(
2019-11-03 00:44:37 DEBUG (SyncWorker_18) [pynuvo] Expecting response from command sent - Data received but no EOL yet :(
2019-11-03 00:44:37 DEBUG (SyncWorker_18) [pynuvo] Expecting response from command sent - Data received but no EOL yet :(
2019-11-03 00:44:37 DEBUG (SyncWorker_18) [pynuvo] Expecting response from command sent - Data received but no EOL yet :(
2019-11-03 00:44:37 DEBUG (SyncWorker_18) [pynuvo] Received buffer: b'#Z1,OFF\r\n'
2019-11-03 00:44:37 DEBUG (SyncWorker_18) [pynuvo] Received: b'#Z1,OFF'
2019-11-03 00:44:37 DEBUG (SyncWorker_18) [pynuvo] CONCERTO_PWR_OFF_PATTERN - Match
2019-11-03 00:44:37 DEBUG (SyncWorker_18) [pynuvo] CONCERTO_PWR_OFF_PATTERN - Match
2019-11-03 00:44:37 DEBUG (SyncWorker_18) [pynuvo] Zone Status Request - Response Invalid - Retry Count: 2
2019-11-03 00:44:38 DEBUG (SyncWorker_18) [pynuvo] Expecting response from command sent - No Data received

Thanks for helping out, and I understand you probably have better things to do.

Iā€™d love to be able to help do what I can so all Nuvo users could use this one day.

Iā€™ve really done next to nothing in python though, but Iā€™m tryingā€¦

Try out this one at https://drive.google.com/open?id=1HVUSMr84OBrD6Nk8N9pZ0EM-cCAMuSEJ

It is not going to work if a zone is powered off, I donā€™t know how to do that part yet, but I think it will detect if a zone is on, get the volume, and let you set the volume and source, and also mute it.

We are both on the same track for the regex. I had just accounted for the extra carriage return. Where my code keeps failing is with the ZoneStatus class. I donā€™t really understand what this part is suppose to do, and it always ends up returning ā€œNoneā€:

    def from_string(cls, string: bytes):
        if not string:
            return None

        match = _parse_response(string)
   
        if not match:
            return None

        try:
           rtn = ZoneStatus(*[str(m) for m in match.groups()])
        except:
           rtn = None
        return rtn

edit:

some logs:

`2019-11-06 20:57:06 INFO (SyncWorker_2) [pynuvo] Sending "Z02STATUS?"
2019-11-06 20:57:06 DEBUG (SyncWorker_2) [pynuvo] Expecting response from command sent - Data received but no EOL yet :(
2019-11-06 20:57:06 DEBUG (SyncWorker_2) [pynuvo] Expecting response from command sent - Data received but no EOL yet :(
2019-11-06 20:57:06 DEBUG (SyncWorker_2) [pynuvo] Expecting response from command sent - Data received but no EOL yet :(
2019-11-06 20:57:06 DEBUG (SyncWorker_2) [pynuvo] Expecting response from command sent - Data received but no EOL yet :(
2019-11-06 20:57:06 DEBUG (SyncWorker_2) [pynuvo] Expecting response from command sent - Data received but no EOL yet :(
2019-11-06 20:57:06 DEBUG (SyncWorker_2) [pynuvo] Expecting response from command sent - Data received but no EOL yet :(
2019-11-06 20:57:06 DEBUG (SyncWorker_2) [pynuvo] Expecting response from command sent - Data received but no EOL yet :(
2019-11-06 20:57:06 DEBUG (SyncWorker_2) [pynuvo] Expecting response from command sent - Data received but no EOL yet :(
2019-11-06 20:57:06 DEBUG (SyncWorker_2) [pynuvo] Received buffer: b'#Z2,OFF\r\n'
2019-11-06 20:57:06 DEBUG (SyncWorker_2) [pynuvo] Received: b'#Z2,OFF'
2019-11-06 20:57:06 DEBUG (SyncWorker_2) [pynuvo] CONCERTO_PWR_OFF_PATTERN - Match - <re.Match object; span=(2, 9), match='#Z2,OFF'>
2019-11-06 20:57:06 DEBUG (SyncWorker_2) [pynuvo] Request return: b'#Z2,OFF'
2019-11-06 20:57:06 DEBUG (SyncWorker_2) [pynuvo] string passed to ZoneStatus.from_string - b'#Z2,OFF'
2019-11-06 20:57:06 DEBUG (SyncWorker_2) [pynuvo] CONCERTO_PWR_OFF_PATTERN - Match - <re.Match object; span=(2, 9), match='#Z2,OFF'>
2019-11-06 20:57:06 DEBUG (SyncWorker_2) [pynuvo] match.groups =- ('2', 'OFF')
2019-11-06 20:57:06 DEBUG (SyncWorker_2) [pynuvo] ZoneStatus rtn - None
2019-11-06 20:57:06 DEBUG (SyncWorker_2) [pynuvo] zone_status rtn: None
2019-11-06 20:57:06 DEBUG (SyncWorker_2) [pynuvo] Zone Status Request - Response Invalid - Retry Count: 1
2019-11-06 20:57:07 DEBUG (SyncWorker_2) [pynuvo] Expecting response from command sent - No Data received
`

Iā€™ve learned in getting mine to work is that the ZoneClass must have the same variables that the regex pattern has assigned to it. Thatā€™s easy on the older Nuvos, since they report the same thing, except OFF vs ON, but on the newer ones they donā€™t return anything else when turned off.

I plan on looking at this some more this week and seeing if another ZoneClass or something can be defined depending on the pattern or something, but Iā€™m not sure, I just donā€™t know enough about python at the moment.

Ideally it would be nice to send a VER command down and find out what the system actually is, and then define classes around that, as well as commands, as they are all a little different. Why in the world Nuvo couldnā€™t just stick to one format is beyond me. I can see them adding stuff to it of course, and new commands, but they just go and practically change the whole thing. Even the newer units donā€™t want a ā€œ0ā€ in front of the zone like on the older ones, at least thatā€™s what the protocol docs say anyway.

@ejonesnospam Any advice you would care to lend or let us know if you would like to see this project continue? I appreciate all of your work and would like to help by submitting pull requests or whatever else I might could do. Youā€™ve started a nice addition that could be enjoyed by many!

So I think I have this working. I was able to init the variables in the class that were missing, this took care of it failing when a zone is off. Then I had to redo all the math in nuvo/media_player.py for the volume.

Now I need to get this instance of HA connected to my main instance of HA. That should be fun.

Iā€™m glad you got it working! In the one I posted to the google drive a few days ago I changed all of the volumes and everything, I hope maybe that was of some help. It should have worked right away for the power on part, just not the power off.

Since the original author is not available anymore, I guess we could look at forking this and merging all of the changes and actually get into HA one day. Let me know your thoughts!

I had to redo the math in the nuvo.py media player integration. My volume goes from 79 to 0 zero, while yours goes from -78 to zero. Those ranges have to be converted to 0 to 1 in nuvo.py. Not having that negative also cleaned up the the code in pynuvo.

https://drive.google.com/drive/folders/1QIO9ROHyX_EblN0XHsFnP4yFNr_Q0xrk?usp=sharing

Nice work!! Were you able to get it to work when the zone power is turned off? One thing I was thinking about doing was sending a *VER down and then basically pulling from that what to expect and send back so it would work with everyoneā€™s system. I guess you could just put the model, as well as the baud rate, in the config file too.

Great work!
i have a setup with a NV-I8GM (concerto) connected to a MPS-4. I want to control the Concerto through Home Assistant setup I started last week.
Would be grateful if you can comment on the cable youā€™re using on the RPi4 (will any USBā€“>RS232 cable work?) and do you connect it with the programming port on the concerto or passthrough on the MPS-4?

have already found instructions on how to install the nuvo media player in HA together with the files you shared in your post.

As a network admin, I just pulled the usb to serial cable out of my bagā€¦ I would assume any brand name cable will work. I have a no name cable that I believe is a counterfeit as the drivers in Windows complain. I donā€™t have a MPS-4 to know how it connects with the Nuvo. I would use a terminal program to manually issue commands when itā€™s passed through or directly connected.

I apologize if this is a very simple question but I am very new to home assistant. I have a working implementation using Node Red but I wanted a tighter integration so I jumped down the rabbit hole of a custom component.

Iā€™ve tried a bunch of implementations with the same result. Currently Iā€™m running Hass.io in a docker container on Ubuntu. I have the files from https://github.com/ejonesnospam/hass.media_player.nuvo which does give me the entity I expect.With only the one entity in the configuration.yaml I get an amazing amount of timeouts in the log which tells me something is wrong.

I took the files from github and placed them in /config/custom_components/nuvo. What am I missing? Iā€™ve looked through the files and there is no mention of baud rate. In other instances I have grabbed pynuvo in a virtual env but I assume the JSON file is taking care of that as Iā€™m seeing pynuvo messages in the log.

Any point in the right direction is appreciated! Thank you all for the work that has been done so far!

So first off from a Home Assistant perspective, you really shouldnā€™t run hass.io in a container. It is a container running docker already. If you have it working I wouldnā€™t worry too much.

I have probably forgot as much as I have learned on this, but I remember one of the components not working on hass version >.92. I donā€™t remember what the exact change was, but I remember I googled the error msg and got right to the answer. I donā€™t think baud rate ever got implemented, or itā€™s in the nuvo.py script.

I am travelling for work right now, so when I get home I can have a look at it more.

Thank you for the reply.

Iā€™m running hass.io in docker. I didnā€™t make a separate container so what I described in my other post is likely inaccurate. Iā€™m still learning all the lingo, Iā€™ve only been at this a couple weeks at this point!

So you are all running on an older version of hass? Seems to me it would make sense to try and update pynuvo if thatā€™s where the issue lies so we can all continue moving forward as the platform continues to evolve. When I have everything I want up and running Iā€™ll work on putting earlier than 0.92 to see if it connects and try and figure out where the disconnect is on the newer versions. Iā€™ve got a lot to learn to make that happen but in the long run it should be beneficial.

No, I was just saying I had to modify one of the scripts to run in versions after .92. I just donā€™t remember the change at the moment.

I found a good usbā€“>rs232 cable that allows me to configure the NV-I8GM with my laptop with the official legrand application.

Unfortunately it doesnā€™t seem to establish a connection or integration between the Raspberry pi4 and the Nuvo.

I followed the steps on the github page, even added your files init.py and replaced mediaplayer.py (I assume they both go in de /custom_components/nuvo/ folder?), but keep getting an error log when rebooting hass.io. :

Setup failed for nuvo: No setup function defined.

Am I doing the right steps? And what should I add to configuration.yaml?

So if anyone is still interested, I have my changes in Git. Both repos can be found here: https://github.com/SmilinJoe The pynuvo repo should be a drop in replacement for that component. The nuvo media_player repo gets added as a custom component. I can help out (now that things are less weird), just let me know.

I donā€™t know if @ejonesnospam wantā€™s to work on incorporating these changes/adds into his code. I am willing to help, but I am not python proficient. Ultimately I think the media player component could be standardized if the math was moved to the pynuvo component. Example if the volume read 0 to 1 instead of -70 to 0, one media_player would work for both platforms.

Like I said, let me know how I can help.

1 Like

HI there

Really appreciate your hard work in this. I tried installing this custom component. I would love to have my nuvo essentia connected to HA.
iā€™m runnning hass.io 0.114.4.
Copied the two hass.media_player.nuvo files from the repo into /custom_config/nuvo/ and updated my Yaml but i get the following error.

ā€˜Logger: homeassistant.setup
Source: setup.py:118
Setup failed for nuvo: No setup function defined.ā€™

Am I doing something wrong? Iā€™m relatively new to HA system.

There are 2 repos. The hass.media_player.nuvo files are the integration. That talks to the pynuvo module to do the rest. There is already a pynuvo installed by default (at least I think itā€™s installed by default), but itā€™s written for a different version of the Nuvo. You need to replace pynuvo/__init__.py thatā€™s installed with the one from my repo.

smilinjoe, thanks for you work on this. For us newbies, where does that new pynuvo file go? I canā€™t find any pynuvo files/folders in the homeassistant directory structure.

That is what I am trying to figure out. I developed this on a Pi using the python virtual env setup. This would put the files at /srv/homeassistant/lib/python3.8/site-packages/pynuvo. I followed Brianā€™s instructions in post 11(Legrand Nuvo multi-room audio support). I am not sure how the hassos handles this now.

EDIT: Looks like we are running into the same issue as these guys: Components require additional python code/library . Since hassio is in a container, you canā€™t access the libraries to modify them. There is discussion there about loading a library with the integration (so my version of pynuvo would live in the custom_component directory). I donā€™t have a good way to test that right now, but you guys can hack away at it.