[Nearing Release] Sonos Advanced Controller

Changing the rule as you indicated, & performing a configure on all Cloud Player devices after updating to the latest bundle seems to have worked. My rule is below:

I also have one set up on a time trigger. Hopefully it works too. The rule above uses a Play:5 as the coordinator, and a Symfonisk Stereo pair as followers.

The other rule uses a Beam as the coord, and a stereo pair of Symfonisk frames. Similar, but just a bit different. The Frame pair was the setup that was acting most strangely, so itll be interesting to see what happens!

Thank you. This integration is brilliant, even in beta!

S

1 Like

Hi @daniel.winks!

Big thanks for the recent updates! I gave the four reported issues a test spin, and happy to report everything is working like a charm now. Kudos! :tada:

I noticed you've also added all the Current States I outlined earlier – much appreciated!

Quick question: Any plans to add this app to the Hubitat Package Manager once it's out of Beta? Would be super convenient!

Thanks again for all your hard work! :rocket:

Yeah, I'm just not sure what all is involved in that. I want to do a few more changes first. Namely, I want to sort out the local control stuff, and move everything that can be done locally to being done locally, including the onboarding of the speakers.

That way the app would have an onboarding that works just like the built-in one, and setting up a developer account, getting the client key/secret, etc, would be entirely optional and only needed for enabling the cloud-dependent non-interrupting TTS.

It'd be really nice if the built-in bundles stuff saved the URL you imported a bundle from, like it does when you import a driver or app via URL. That would make updating bundles much easier, to the point that the built-in bundles would be a solid enough to not need HPM.

I see that there is a workaround for audio clip endpoint that is local only. It would be AWESOME if we ca achieve everything without the need for cloud API.

3 Likes

New bundle on GitHub.

Changes:

  • Moved a lot of the XML processing into the parent app, which results in much better handling of "update the follower players with the coordinator player's info", as well as allowing for quicker short-circuiting of processing, reducing CPU usage a bit.

  • Fixes to event subscription/resubscription. Should properly resubscribe every 10 minutes, to keep speakers in-sync with current state properly.

  • Other fixes and improvements.

Hi Daniel,

Thanks to @DGBQ pointing me this way toward your Driver and App - I've gone ahead and have it all running on HE with AudioClip working.

I've noticed that for some reason there is now a 4-5 second delay between my WebCoRE piston calling the audioClip and hearing it - not sure if this is down to the Hubitat Hub's reduced CPU versus the server running the Node.JS plugin, or something in the driver.

I've still got some work to do to get my buttons working properly in the Bathroom and En-suite using this device code, at the moment when I call my Sonos Favourite it doesn't seem to play radio stations.

Thanks for this - very useful and much better than the HE Built-in SONOS App :+1:

1 Like

Odd, I'm using mine for my doorbell, which is a ZigBee button. From the time the button is pressed until when the (stored locally on my hubitat) MP3 is played is no more than 1 second, probably around half a second. It's not instant, but it's close to it. There's some delay, but it does have to send things to the Sonos Cloud and back down again, and then the speakers themselves take a bit of time to play too. Definitely not 4-5 seconds.

There's very very little done on Hubitat to make the TTS call. It's just the child device calling a parent method (which should take close to 0 time), then the parent method. The parent method just does a few lines of stuff, getting the playerId from the child device, building a 3 or 4 element Map object with a couple of values, then calling the http endpoint on Sonos. It's definitely not doing anything remotely heavy for this.

Pretty much the only sources of any appreciable delay would be WebCoRE, the Sonos Cloud, and the speaker responding to whatever Sonos Cloud sends it. Seeing as the only difference between what you're doing and what I do is you're using WebCoRE and I'm not, then perhaps that's causing a delay. I'm using Rule Machine for all of my stuff, and it's fast.

Do you see this same delay if you just do "play track" with an "http://..." address of an MP3 from one of the speaker devices themselves?

Hey @daniel.winks

Did I miss something? Didn't you have some track data on each device before this upgrade in Current State? Or am I mixing that up with the Hubitat app? I thought for sure I saw it and was about to add it to my Webcore pistons.

I do, there's an optional toggle for "Disable Track Data Events". I have it off by default since it's a JSON object and a pain to work with. Everything that's exposed by turning that toggle on is also just exposed as straight one-item string attributes too. So if you want to do something with track number, artist name, etc, you can do that just using the attributes for those, rather than having to parse them out of the trackData blob.

But if you turn that toggle on, it gives what I can best determine the built-in one does.

I'd personally recommend using the individual states for whatever you're wanting, they're easier to deal with, IMO.

I put in quite a bit of changes/fixes/enhancements on "current state" stuff today, so I'd also recommend grabbing the latest bundle if you haven't. It has the current state stuff much improved, especially for 'follower' speakers.

Edit: what in the trackData are you utilizing? I can make sure if there's anything missing from within it that needs added as a plain attribute I can get it added.

Hi @daniel.winks,

A massive thanks for the recent update – your hard work is genuinely appreciated!

I've been playing around with the "disable artist, album, and track events" toggle, and it's a fantastic addition. However, I've noticed a bit of instability. While it works some of the time, there are moments when it seems a bit temperamental. Currently, I'm struggling to get it to display artist, album, and track events reliably – it's stuck On but not displaying any of the related Current states. I have tried turning it On or Off and saving the preferences in between with no luck.

I've also run into a minor glitch in my testing. After using the "disable artist, album, and track events" toggle, it can occasionally locks up my Sonos system. For example, when trying to change the volume or switch to a new favorite in the device, it just won't respond using the device. The workaround involves ungrouping all players in the Sonos app, then using the Cloud Group device to join the player back to the coordinator. That usually gets things back into gear, and I can resume using the integration for actions like Stop, Play, changing the volume, selecting a favorite, etc.

Now, onto my quest: I'm on the lookout for a way to capture and display the current favorite being used. In Webcore (in the past), I've set up a piston to check the specific favorite playing at a certain time and switch if needed (mostly radio stations). In SmartThings and Hubitat's app, I used something like audioTrackData to identify if a particular favorite was on by the radio’s displayed station name. I hoped to use "currentAlbumName" to cover this when you can fix it, or is there another way to get this data in the Current States? Additionally, I'd love to display the Favorite in a Dashboard tile. I trust this is as clear as mud.

Any thoughts on achieving this without a major overhaul? I'm loving the current ability to Group and Pick Favorites, just hoping to add a bit more functionality to the setup where possible.

Thanks a bunch for your continued support! :blush:

Daniel,

I just updated the bundles, and hit "configure" on all the Cloud Players, and my logs picked some interesting entries...

Summary

512024-01-02 06:23:05.472 PMerrorjava.lang.NullPointerException: Cannot invoke method updateDataValue() on null object on line 921 (method processZoneGroupTopologyMessages)

app:512024-01-02 06:23:05.092 PMerrorjava.lang.NullPointerException: Cannot invoke method tokenize() on null object on line 827 (method processAVTransportMessages)

app:512024-01-02 06:23:01.140 PMerrorjava.lang.NullPointerException: Cannot invoke method updateDataValue() on null object on line 921 (method processZoneGroupTopologyMessages)

app:512024-01-02 06:23:00.743 PMerrorjava.lang.NullPointerException: Cannot invoke method tokenize() on null object on line 827 (method processAVTransportMessages)

app:512024-01-02 06:23:00.421 PMerrorjava.lang.NullPointerException: Cannot invoke method updateDataValue() on null object on line 921 (method processZoneGroupTopologyMessages)

app:512024-01-02 06:22:59.637 PMerrorjava.lang.NullPointerException: Cannot invoke method updateDataValue() on null object on line 921 (method processZoneGroupTopologyMessages)

app:512024-01-02 06:22:59.236 PMdebugSonos Cloud Controller: Just removed from group!

app:512024-01-02 06:22:55.981 PMerrorjava.lang.NullPointerException: Cannot invoke method updateDataValue() on null object on line 921 (method processZoneGroupTopologyMessages)

app:512024-01-02 06:22:55.109 PMerrorjava.lang.NullPointerException: Cannot invoke method updateDataValue() on null object on line 921 (method processZoneGroupTopologyMessages)

app:512024-01-02 06:22:54.676 PMdebugSonos Cloud Controller: Just removed from group!

app:512024-01-02 06:22:49.007 PMerrorjava.lang.NullPointerException: Cannot invoke method updateDataValue() on null object on line 921 (method processZoneGroupTopologyMessages)

app:512024-01-02 06:22:48.524 PMerrorjava.lang.NullPointerException: Cannot invoke method tokenize() on null object on line 827 (method processAVTransportMessages)

app:512024-01-02 06:22:43.920 PMerrorjava.lang.NullPointerException: Cannot invoke method updateDataValue() on null object on line 921 (method processZoneGroupTopologyMessages)

app:512024-01-02 06:22:43.631 PMerrorjava.lang.NullPointerException: Cannot invoke method tokenize() on null object on line 827 (method processAVTransportMessages)

app:512024-01-02 06:22:43.351 PMdebugSonos Cloud Controller: Current group name: Loft Current coordinator: Loft Current group member count: 1

app:512024-01-02 06:22:43.349 PMdebugSonos Cloud Controller: Current Group Member IDs: [Loft]

app:512024-01-02 06:22:42.937 PMerrorjava.lang.NullPointerException: Cannot invoke method updateDataValue() on null object on line 921 (method processZoneGroupTopologyMessages)

app:512024-01-02 06:22:42.276 PMdebugSonos Cloud Controller: Just removed from group!

app:512024-01-02 06:22:38.347 PMdebugSonos Cloud Controller: Current group name: Loft Current coordinator:

I dunno if its important, but thought it was worth saving, just in case.

S.

I may have your quest at an end:

Latest version on GitHub stores a Map of favorites (updated every 3 hours), and upon each "AVTransport" it checks what's on 'enqueuedUri' and if it matches a favorite, it displays that favorite. If there a non-favorite playing, it has 'null'.

To get this working, beyond updating to the latest bundle, you need to open the app (just to the first page) and click on "Done" at the bottom. This invokes the "configure()" method on the app, which is where I added the call to appGetFavorites() and a scheduled refresh for that method every 3 hours. Or you can click on "getFavorites()" from any player device.

Both will cause the app to pull in a cached Map and schedule the update for every 3 hours. getFavorites() from a player device will cause the app to update its cached copy and start the 3 hour timer over, too, in cause you need to update it immediately for any reason. Not sure if I should make that refresh interval configurable or not.

As for the toggle issue, I noticed some goofiness myself earlier when toggling that on/off. I changed how it clears those attribute states out, not sure if that helps or not, but I think it may. I'll look into it. a bit more later.

Please update to the latest bundle on GH and let me know if this happens again. I may have got that fixed.

1 Like

New bundle on GitHub.

Changes:

  • Added local control for setting player volume and mute/unmute. Will add more local control in the near future. Let me know if the player volume has any issues.

  • Added "currently playing favorite" state. If you've got a favorite playing, it should show you the name and album art for the favorite. You need to click on "getFavorites()" from any player device once to build the initial cache needed for matching. The matching cache updates every 3 hours, and can be updated immediately by calling "getFavorites()" again from any player. Let me know if there's a need to adjust the update interval or change how an immediate refresh can be performed.

Will do.

...it's different, seems a bit better.

Summary

512024-01-02 09:05:00.320 PMerrorjava.lang.NullPointerException: Cannot invoke method updateDataValue() on null object on line 990 (method processZoneGroupTopologyMessages)

dev:1112024-01-02 09:04:59.723 PMerrorgroovy.lang.MissingMethodException: No signature of method: user_driver_dwinks_Sonos_Cloud_Player_703.setCurrentArtistAlbumTrack() is applicable for argument types: (null, null, null, java.lang.String) values: [null, null, null, 0]
Possible solutions: setCurrentArtistAlbumTrack(java.lang.String, java.lang.String, java.lang.String, java.lang.Integer) on line 943 (method setCurrentArtistAlbumTrack)

dev:1142024-01-02 09:04:59.629 PMerrorgroovy.lang.MissingMethodException: No signature of method: user_driver_dwinks_Sonos_Cloud_Player_703.setCurrentArtistAlbumTrack() is applicable for argument types: (null, null, null, java.lang.String) values: [null, null, null, 0]
Possible solutions: setCurrentArtistAlbumTrack(java.lang.String, java.lang.String, java.lang.String, java.lang.Integer) on line 943 (method setCurrentArtistAlbumTrack)

app:512024-01-02 09:04:38.408 PMerrorjava.lang.NullPointerException: Cannot invoke method keySet() on null object on line 905 (method processAVTransportMessages)

app:512024-01-02 09:04:37.329 PMerrorjava.lang.NullPointerException: Cannot invoke method updateDataValue() on null object on line 990 (method processZoneGroupTopologyMessages)

app:512024-01-02 09:04:32.444 PMdebugSonos Cloud Controller: Current group name: Loft Current coordinator: Loft Current group member count: 1

app:512024-01-02 09:04:32.442 PMdebugSonos Cloud Controller: Current Group Member IDs: [Loft]

dev:1102024-01-02 09:04:32.165 PMerrorgroovy.lang.MissingMethodException: No signature of method: user_driver_dwinks_Sonos_Cloud_Player_703.setCurrentArtistAlbumTrack() is applicable for argument types: (null, null, null, java.lang.String) values: [null, null, null, 0]
Possible solutions: setCurrentArtistAlbumTrack(java.lang.String, java.lang.String, java.lang.String, java.lang.Integer) on line 943 (method setCurrentArtistAlbumTrack)

dev:102024-01-02 09:03:20.775 PMinfoBMT Bath Humidity Sensor 1 on Hub-3: battery is 100%

dev:102024-01-02 09:03:07.962 PMerrorjava.lang.NumberFormatException: null on line 197 (method parse)

S.

1 Like

**Edit: Found the Issue - My ISP uses 1.1.1.1 as it's default DNS service (CloudFlare) which adds latency calling the SONOS API, changed this to OpenDNS and Google and all works immediately now

Hi Daniel,

I've just done this video comparing the 2 actions - the first is using the 'Play Track and Restore' via the device page in HE using the local URL to the audio clip, and the second is using the Node AudioClip API HTTP call to play the same clip:

As you can see the action from the device page is around 4-5 seconds delayed with the Node HTTP call being instant.

Let me know if there's anything else you want me to try?

Best wishes,
Guy

That's really odd that it would cause latency issues. CloudFlare's DNS is really good, and their 1.1.1.1 service doesn't do any sort of filtering. My Hubitat has the example DNS overrides set (8.8.8.8, 8.8.4.4, 1.1.1.1, 1.0.0.1, 9.9.9.9), because my LAN has a rather restrictive adblocking DNS on it, and I don't want to deal with issues arising from having DNS queries on my Hubitat getting blocked. Assuming Hubitat doesn't use these round-robin and is fail-over/fallback instead, then unless both of Google's DNS are down I'd never have used CloudFlare's.

I'll be adding local non-interrupting TTS soon anyway, that won't need any DNS lookups. Apparently both the WSS endpoint and the REST endpoint on (S2 only) Sonos speakers accepts pretty much the exact same requests that their Cloud API does. It is S2 only, however, so I'll need to make sure that S1 speakers aren't using it. I don't have any S1 speakers, but my S2 ones all have "swGen: 2" on their Data section (which is pulled from the speaker when being set up). So if you (or anyone else here) has some S1 speakers, let me know if that says "swGen: 1" there, so I can use that to if/else the logic on which TTS to use.

I've already moved the "player set level" over to using the SOAP/UPnP local controls and will move over everything else that can be controlled locally to that now that I have it figured out. That should be S1 and S2 compatible, but it doesn't have the non-interrupting TTS, as that's an S2 only feature apparently (S2 only, locally, that is).

3 Likes

Hey @daniel.winks,

Love the look of that currentFavorite feature! Quick question: does it show the radio station when it's the selected Favorite?

I ran into a little hiccup after applying your latest update. The currentFavorite isn't displaying, and Get Favorites isn't working anymore. I followed your instructions to open the app to the first page and click on "Done" at the bottom, but no luck.

This is the Current States for one of my players as an example:

Current States

  • bass : 0
  • currentAlbumName : SB00009
  • currentCrossfadeMode : off
  • currentRepeatAllMode : off
  • currentRepeatOneMode : off
  • currentShuffleMode : off
  • currentTrackDuration : 0:00:00
  • currentTrackNumber : 1
  • groupCoordinatorName : Kitchen Player
  • groupMemberCount : 2
  • groupMemberNames : [Kitchen Player, Office Player]
  • groupName : Kitchen Player + 1
  • isGroupCoordinator : on
  • isGrouped : on
  • level : 25
  • loudness : off
  • mute : unmuted
  • status : playing
  • transportStatus : playing
  • treble : 0
  • volume : 25
  • currentArtistName : null
  • currentTrackName : null

Another interesting note is that

  • groupName : Kitchen Player + 1
    Is not correct I have 8 total players in the group.
    2024-01-03 07_46_53-Sonos

Also looking at the Bathroom for example this is the Current States

Current States

  • bass : 0
  • currentAlbumName : SB00009
  • currentCrossfadeMode : off
  • currentRepeatAllMode : off
  • currentRepeatOneMode : off
  • currentShuffleMode : off
  • currentTrackDuration : 0:00:00
  • currentTrackNumber : 1
  • groupCoordinatorName : Bathroom Player
  • groupMemberCount : 1
  • groupMemberNames : [Bathroom Player]
  • groupName : Bathroom Player
  • isGroupCoordinator : on
  • isGrouped : off
  • level : 32
  • loudness : off
  • mute : unmuted
  • status : playing
  • transportStatus : playing
  • treble : 0
  • volume : 32

Looking at the Laundry Room this is the Current States

Current States

  • bass : 0
  • currentAlbumName : ppm-jazz24mp3-ibc1
  • currentCrossfadeMode : off
  • currentRepeatAllMode : off
  • currentRepeatOneMode : off
  • currentShuffleMode : off
  • currentTrackDuration : 0:00:00
  • currentTrackNumber : 1
  • groupCoordinatorName : Laundry Player
  • groupMemberCount : 1
  • groupMemberNames : [Laundry Player]
  • groupName : Laundry Player
  • isGroupCoordinator : on
  • isGrouped : off
  • level : 26
  • loudness : off
  • mute : unmuted
  • status : playing
  • transportStatus : playing
  • treble : 0
  • volume : 26

Any suggestions on how to troubleshoot and fix this?

You could give a "configure()" on each player a try. I'm still working on getting the subscriptions to work 100%... seems like they fail to resubscribe every so often. Without the subscriptions working, the players don't get updates as they should.

Are there any errors in the logs when you click on getFavorites?

Hi @daniel.winks

Each time I update I have selected on each device Refresh, Configure, Subscribe to Events.

Here's part of the Laundry Player log

dev:4542024-01-03 10:28:18.563 AMwarnmethod parse of child device Sonos Cloud - Laundry Player ran for 120,544ms

dev:4542024-01-03 10:24:09.397 AMerrorgroovy.lang.MissingMethodException: No signature of method: user_driver_dwinks_Sonos_Cloud_Player_846.setCurrentArtistAlbumTrack() is applicable for argument types: (java.lang.String, java.lang.String, java.lang.String, java.lang.String) values: [, , , 0]
Possible solutions: setCurrentArtistAlbumTrack(java.lang.String, java.lang.String, java.lang.String, java.lang.Integer) on line 943 (method setCurrentArtistAlbumTrack)

dev:4542024-01-03 10:04:09.263 AMerrorgroovy.lang.MissingMethodException: No signature of method: user_driver_dwinks_Sonos_Cloud_Player_846.setCurrentArtistAlbumTrack() is applicable for argument types: (java.lang.String, java.lang.String, java.lang.String, java.lang.String) values: [, , , 0]
Possible solutions: setCurrentArtistAlbumTrack(java.lang.String, java.lang.String, java.lang.String, java.lang.Integer) on line 943 (method setCurrentArtistAlbumTrack)

dev:4542024-01-03 09:44:09.418 AMerrorgroovy.lang.MissingMethodException: No signature of method: user_driver_dwinks_Sonos_Cloud_Player_846.setCurrentArtistAlbumTrack() is applicable for argument types: (java.lang.String, java.lang.String, java.lang.String, java.lang.String) values: [, , , 0]
Possible solutions: setCurrentArtistAlbumTrack(java.lang.String, java.lang.String, java.lang.String, java.lang.Integer) on line 943 (method setCurrentArtistAlbumTrack)

dev:4542024-01-03 09:24:09.471 AMerrorgroovy.lang.MissingMethodException: No signature of method: user_driver_dwinks_Sonos_Cloud_Player_846.setCurrentArtistAlbumTrack() is applicable for argument types: (java.lang.String, java.lang.String, java.lang.String, java.lang.String) values: [, , , 0]
Possible solutions: setCurrentArtistAlbumTrack(java.lang.String, java.lang.String, java.lang.String, java.lang.Integer) on line 943 (method setCurrentArtistAlbumTrack)

dev:4542024-01-03 09:04:09.288 AMerrorgroovy.lang.MissingMethodException: No signature of method: user_driver_dwinks_Sonos_Cloud_Player_846.setCurrentArtistAlbumTrack() is applicable for argument types: (java.lang.String, java.lang.String, java.lang.String, java.lang.String) values: [, , , 0]
Possible solutions: setCurrentArtistAlbumTrack(java.lang.String, java.lang.String, java.lang.String, java.lang.Integer) on line 943 (method setCurrentArtistAlbumTrack)

dev:4542024-01-03 08:44:09.419 AMerrorgroovy.lang.MissingMethodException: No signature of method: user_driver_dwinks_Sonos_Cloud_Player_846.setCurrentArtistAlbumTrack() is applicable for argument types: (java.lang.String, java.lang.String, java.lang.String, java.lang.String) values: [, , , 0]
Possible solutions: setCurrentArtistAlbumTrack(java.lang.String, java.lang.String, java.lang.String, java.lang.Integer) on line 943 (method setCurrentArtistAlbumTrack)