Retain delta volume for Sonos speaker group


it bothered me for years already that carefully adjusted volume differences between Sonos speakers in a group are only retained if I use the Sonos controller app. In this case, the group volume can be changed and the initially adjusted difference between speaker volumes of the group stays the same. However, as soon as somebody changes the volume directly on the buttons of a speaker (as my family tends to do :wink: ), only that speaker changes its volume and everything is mis-adjusted again. (I filed two requests with Sonos on making speaker changes optionally apply to the whole group, but seemingly this wasn’t considered important so far…)
I found that I could use OpenHAB to get better control about that, and it works so far most of the time with two rules that observe changes on the zone members and then actively set the desired delta for the others. I thought that I took measures against endless loops by checking against the actual values of the speakers and not initiating another change if not required, however sometimes the whole system gets into an oscillation state where the volume is going up and down due to OpenHAB rules. The only way to get this stopped is restarting the Raspberry then. Changing the scripts on the fly is also no longer considered then…
Has anyone a clue under what conditions this setup is going crazy, and might have a solution? I am honestly an OpenHAB newbie for now and maybe missed something crucial for such a scenario…

These are my two rules:

var Number volumeDelta = 5

rule "Sonos volume delta kitchen"
    Item VolumeSonosKitchen changed
	var Number volKitchen = VolumeSonosKitchen.state as DecimalType
	var Number volLivingroom = VolumeSonosLivingroom.state as DecimalType
	var Number newVolLivingroom = volKitchen + volumeDelta
	if (newVolLivingroom > 100) newVolLivingroom = 100
	if (newVolLivingroom != volLivingroom) {
		sendCommand(VolumeSonosLivingroom, newVolLivingroom)

rule "Sonos volume delta living room"
    Item VolumeSonosLivingroom changed
	var Number volKitchen = VolumeSonosKitchen.state as DecimalType
	var Number volLivingroom = VolumeSonosLivingroom.state as DecimalType
	var Number newVolKitchen = volLivingroom - volumeDelta
	if (newVolKitchen < 0) newVolKitchen = 0
	if (newVolKitchen != volKitchen) {
		sendCommand(VolumeSonosKitchen, newVolKitchen)

In words, the first rule will INCREASE the volume in the living room if the volume in the kitchen is changed (up or down)
The second rule will DEC REST the volume in the kitchen if the volume in the living room is changed.
Is that what you want. Or am I missing something?

If the volume in the kitchen is changed, the volume in the livingroom is always set 5 ticks higher than the kitchen player. Accordingly, any change in the livingroom sets kitchen to that value minus 5. So the kitchen Play:1 should always be 5 points lower than the Play:5 in the livingroom.
The scripts actually works as intended “most of the time”, but sometimes goes wild as described above. Possibly some race conditions, or as well possible that I am not completely up to speed how and when rules are evaluated or what other side effects I need to consider.

Sorry, I did misunderstand the code!

For the race-condition:
As long as you are using a such two rules, both which are reacting on a change and that sending a comand to change to the other, you can’t really eliminate such a race condition!

Possible Solution: Do NOT react on changes of the volume, create a rule that checks in fixed timesteps wether the volume settings correspond correctly, if not reset your desired relation using the setting that was last changed as the base (you would need a persitence for every change of the volume channels).
That migth be over powered (or better using to much system capacity), but that is your decision.

When I manage to get the persistence right, I might also try to set a flag when something was sent and then ignore rule execution (and reset flag) on next iteration…