UPDATE This post is deprecated. See the following post for the new rule that is compatible with openHAB 3 https://community.openhab.org/t/sonos-multiroom-rule-for-openhab-3/108305
UPDATE: I made some changes on the sitemaps and also some corrections in t´he rule, so please update your files. The rule can be replaced, but ceep your configuration of the Userinput area. Additionally I added some screenshots of the sitemap.
Hello together,
today I want to share my Sonos Multiroom Control rule. I designed this because I don’t want to control my sonos devices fom the sonos app. I want to control all devices from my sitemaps. Also I will not write for each room a rule that checks if some other room is currently active. So I come up to the decision that I have to write a rule that can manage all this in one. The only thing have to do in my presence detection rules of each room ist o provide the information if the player should be started or stopped. All other things like grouping and Volume Control for each room should be done automatically. Now this rule is running quite well. Of course I’m still in testing but maybe someone of you is interested on such a rule. So I decided to provide it to you.
And if this rule is in the wrong category, please admins move it to the right position. Thanks
The rule is able to manage the grouping of zones. You can check if some player is currently active, if yes, the given player will be added (Multiroom). If no player is available, a default uuri will be played (Play) Of course you can add some own code to the play section if you know what you do ;). Additionally, you can easily remove a player from the zone, unless if he is the zone coordinator or a player. The rule will manage it automatically (Remove). Then you can make a player to a standalone player with one command including the remove of all other players (Standalone). The next thing I added was the possibility to ungroup inactive players automatically if they are inactive for 60 seconds. I found this very useful because in some cases I switched from sonos to iPhone and back to sonos. Then the music was in all rooms and not only in the room where I was because I was not ungrouping the devices during the switch to my iPhone. After this my grouping rule worked quite well, but there was the next problem. If I control the volume from my sitemap, I have to manage each room itself. So I added some Volume feature to the rule that can easily change the volume of all devices if one device changes (MultiroomVolume). But then I realized that it makes no sense to have the same volume in each room. There is always some offset from room to room needs because in each room the given volume is different because of the amount of speakers and so on. So I added the function of an offset. After this I recognized that not always a player is active and if no player is active, how can I define a default volume. This is needed to avoid extreme loud music if I go. in the kitchen in the night and the music starts with the last given volume… This feature can be called manually or if you use MultiroomVolume and no player is available. it will automatically use the default volume that is defined for each hour of the day. Of course it will also use the offset of each zone (DefaultVolume). Then I also want to disable the Multiroom Volume for one specific room if needs, so I also added this feature. Now this is the current status and maybe you have some more ideas that should be added to this rule.
To control the the rule, there are different things needed. For the Volume control, you have to define a Offset for the room in the “SonosZone_Offset” Item. Additionally, you have to enable the Volume feature with the Switch “SonosZone_MultiroomVolume”. To control the grouping, you have to use the Item “SonosMultiroom”. You can send a String command to it. This string can contain one to more commands and one or more Sonos Players. You can use the following commands Multiroom, Remove, Standalone, MultiroomVolume, DefaultVolume and Play for more detils look in the description. After each feature description the feature is visible in (). The Syntax is the following:
Command/Command…/Devices=Zone1/Zone2…
So for example: Multiroom/MultiroomVolume/Devices=Livingroom
This will check if currently some zone is running, if yes, it will add Livingroom to the zone and change the volume of the zone to active zone. Thereby the offset of both zones will be used! If no player is available, it will use a predefined uuri that you have to defined in the rule itself. (maybe I will change this to a item so that you can change the uuin during the runtime. But again it is only used if no player is available.) If there was no zone active before it will use the Defaultvolume that is predefined in the rule for each hour of the day. Of course the offset of the player will be used again.
To get this rule running, you need the following Sonos Items. Some of them are general Items and some are needed for each device. The name Syntax is always the same (“Sonos” + Zonename + “_Channelname”). It is important that you are using the same groups like I have defined them. Of course you can add additional groups if they are needed from your current sitemaps or rules. Then there are some Items that need a link to a channel of a sonos device and some are needs for rules and so there is no sonos channel available for them (so don’t be confused).
I recommend to use a persistence Service for the items of group gSonosMultiroom* to avoid a reset after a reboot.
Items:
General Items that are only needed once:
Group gSonos
Group:Dimmer:AVG gSonosVolume (gSonos)
Group:Number:COUNT("PLAYING") gSonosState (gSonos)
Group:Number:COUNT("ON") gSonosMultiroom (gSonos)
Group:Number:AVG gSonosMultiroomOffset (gSonos)
String SonosMultiroom <mediacontrol> (gSonos)
Switch SonosMultiroomUpdatePlayers //Do not use this item manually!!!
Items that you need for each room. The example is now with the Livingroom:
Switch SonosLivingroom_MultiroomVolume <switch> (gSonosMultiroom)
Dimmer SonosLivingroom_Offset <soundvolume> (gSonosMultiroomOffset)
Dimmer SonosLivingroom_Volume <soundvolume> (gSonosVolume) { channel="" }
Player SonosLivingroom_Control <player> (gSonosState) { channel="" }
Image SonosLivingroom_CurrentAlbumArt (gSonos) { channel="" ]
String SonosLivingroomr_CurrentArtist (gSonos) { channel="" }
String SonosLivingroom_Add (gSonos) { channel="" }
String SonosLivingroom_Coordinator (gSonos) { channel="" }
Switch SonosLivingroom_Mute (gSonos) { channel="" }
String SonosLivingroom_PlayURI (gSonos) { channel="" }
String SonosLivingroom_Remove (gSonos) { channel="" }
String SonosLivingroom_Repeat (gSonos) { channel="" }
Switch SonosLivingroom_Shuffle (gSonos) { channel="" }
Switch SonosLivingroom_StandAlone (gSonos) { channel="" }
Some excample siemps:
Player inactive:
Player active
Multiroom Control options:
Frame label="Sonos"{
Default item=SonosLivingroom_Control label="Player" icon="player"
Text icon="none"
Selection item=SonosMultiroom label="Multiroom Control" icon="mediacontrol" mappings=["Multiroom/MultiroomVolume/Devices=Livingroom"="Add to/Create Multiroom Group",
"Standalone/Devices=Livingroom"="Set to Standalone", "MultiroomTarget/DefaultVolume/Devices=Livingroom/Bedroom"="Add Bedroom to Group",
"MultiroomTarget/Devices=Livingroom/Bathroom"="Add Bathroom to Group", "EnableMultiroomVolume/Devices=Livingroom"="Enable Multiroom Volume",
"DisableMultiroomVolume/Devices=Livingroom"="Disable Multiroom Volume"]
Switch item=SonosLivingroom_Repeat label="Repeat: " visibility=[SonosLivingroom_Control=="PLAY"] mappings=[ALL="All", OFF="None", ON="This track"]
Switch item=SonosLivingroom_MultiroomVolume label="Multiroom Volume" mappings=[ON="On", OFF="Off"]
Switch item=SonosLivingroom_Shuffle label="Shuffel: " visibility=[SonosLivingroom_Control=="PLAY"] mappings=[ON="On", OFF="Off"]
Slider item=SonosLivingroom_Volume label="Volume" icon="soundvolume"
Text item=SonosLivingroom_CurrentTitle label="Titel: " visibility=[SonosLivingroom_Control=="PLAY"]
Slider item=SonosLivingroom_Offset label="Offset" icon="soundvolume"
Text item=SonosLivingroom_CurrentArtist label="Artist: " visibility=[SonosLivingroom_Control=="PLAY"]
Switch item=SonosLivingroom_Mute label="Mute: " visibility=[SonosLivingroom_Control=="PLAY"] mappings=[ON="On", OFF="Off"]
Text item=SonosLivingroom_CurrentAlbum label="Album: " visibility=[SonosLivingroom_Control=="PLAY"]
Image item=SonosLivingroom_CurrentAlbumArt label="Cover" visibility=[SonosLivingroom_Control=="PLAY"]
}
Now you are almost done. The last thing is the rule file. In this you have to define for each Room (like Livingroom), the room name and the uuri of the sonos player. Therefore search for the field SonosPlayer and SonosUURI, they are in the User Input section. Then add each Player and uri separated. I is important that the uuri and the playername are on the same position in the array. One example with three rooms is available in the rule. Just replace the three rooms with your rooms, of yourse you can add so many rooms like you need. Then you have to define a default volume for each hour in the DefaultVolume array. The first entry is for 00:00, the next entry for 01:00, 02:00…23:00 so you should have 2 entries. As last step you have to define the default play uri that should be used if no zone is active. I used a radio uri.
The rule will be splitted in two parts, because it is to big for one post:
import java.util.List
import java.util.ArrayList
import org.eclipse.xtext.xbase.lib.Functions
import java.util.concurrent.locks.ReentrantLock
import org.eclipse.xtext.xbase.lib.Procedures
import org.eclipse.smarthome.model.script.actions.Timer
//Userinput needed:
val ArrayList<String> SonosPlayer = newArrayList("Livingroom", "Bedroom", "Bathroom")
val ArrayList<String> SonosUUID = newArrayList("RINCON_XXAAFDFXX2CC01400", "RINCON_XX9F3EFEXX2801400", "RINCON_XX28CAXXCAAE01400")
val List<Integer> DefaultSpeakerVolume = newArrayList(8, 8, 8, 8, 8, 8, 8, 10, 12, 14, 16, 18, 18, 20, 20, 20, 20, 18, 18, 16, 14, 12, 10, 8)
val String DefaultPlayUri = "x-rincon-mp3radio://http://hr-youfm-live.cast.addradio.de/hr/youfm/live/mp3/128/stream.mp3?ar-distributor=f0a1"
//Userinput end, please do not change settings below if you are not sure that you know what you arre doing!
var Boolean firstRun = true
var Boolean MultiroomVolumeServiceRunning = false
var List<Integer> PlayerStatusUpdateState = newArrayList(0)
var List<Integer> MultiroomVolumeOffset =newArrayList(0)
var List<Integer> MultiroomVolumeAvailable = newArrayList(0)
val ReentrantLock PlayerStatusUpdateLock = new ReentrantLock()
val ReentrantLock MultiroomVolumeLock = new ReentrantLock()
var Timer AutoremoveTimer = null
val Functions$Function1<String, Boolean> prerequiremets= [ s |
var JobSuccesfull = true
if(gSonosState.state === NULL) {
logError("SonosMultiroom.Service", "Please check input items Sonos..._State. The import of the groupitem.state failed.")
JobSuccesfull = false
}
if(gSonosVolume.state === NULL) {
logError("SonosMultiroom.Service", "Please check input items Sonos..._Volume. The import of the groupitem.state failed.")
JobSuccesfull = false
}
if(gSonosMultiroom.state === NULL) {
logError("SonosMultiroom.Service", "Please check input items Sonos..._MultiroomVolume. The import of the groupitem.state failed.")
JobSuccesfull = false
}
if(gSonosMultiroomOffset.state === NULL) {
logError("SonosMultiroom.Service", "Please check input items Sonos..._Offset. The import of the groupitem.state failed.")
JobSuccesfull = false
}
if(JobSuccesfull == false) {
logError("SonosMultiroom.Service", "Sonos Multiroom stops because not all prerequirements are available.")
true
} else {
false
}
]
val Functions$Function1<List<String>, List<Integer>> initMultiroomVolumeAvailable= [ myArray |
var Number Cursor = 0
var String Command
var List<Integer> functionArray = newArrayList(0)logInfo("SonosMultiroom.Service", "Initialise Sonos Multiroom Service")
while(Cursor < myArray.size()) {
Command = "Sonos" + myArray.get((Cursor).intValue) + "_MultiroomVolume"
val ImportMultiroom = gSonosMultiroom.members.findFirst[ i | i.name == Command ]
if(ImportMultiroom.state == ON) {
Command = "Sonos" + myArray.get((Cursor).intValue) + "_Volume"
val ImportVolume = gSonosVolume.members.findFirst[ i | i.name == Command ]
functionArray.add(((Cursor).intValue), (Integer::parseInt(ImportVolume.state.toString)))
} else {
logInfo("SonosMultiroom.Service", "Multiroom Volume is inactive in Zone " + myArray.get(Cursor.intValue) + ".")
functionArray.add(((Cursor).intValue), -1)
}
Cursor = Cursor+1
}
functionArray
]
val Functions$Function1<List<String>, List<Integer>> initMultiroomVolumeOffset= [ myArray |
var Number Cursor = 0
var String Command
var List<Integer> functionArray = newArrayList(0)
while(Cursor < myArray.size()) {
Command = "Sonos" + myArray.get((Cursor).intValue) + "_Offset"
val ImportOffset = gSonosMultiroomOffset.members.findFirst[ i | i.name == Command ]
functionArray.add(((Cursor).intValue), (Integer::parseInt(ImportOffset.state.toString)))
Cursor = Cursor+1
}
functionArray
]
val Functions$Function1<List<String>, List<Integer>> initPlayerStatusUpdateState= [ myArray |
var Number Cursor = 0
var String Command
var List<Integer> functionArray = newArrayList(0)
logInfo("SonosMultiroom.Service", "Initialise Sonos Player Update Service")
while(Cursor < myArray.size()) {
Command = "Sonos" + myArray.get((Cursor).intValue) + "_Control"
val ImportControl = gSonos.members.findFirst[ i | i.name == Command ]
if(ImportControl.state == PLAY) {
functionArray.add((Cursor.intValue), 0)
}
if(ImportControl.state != PLAY) {
functionArray.add((Cursor.intValue), -1)
}
Cursor = Cursor +1
}
functionArray
]
val Functions$Function2<List<String>, String, Number> getPlayerID= [ myArray, player |
var Number Cursor = 0
var Number Pointer
while(Cursor < myArray.size()) {
if(player == myArray.get(Cursor.intValue)) {
Pointer = Cursor
Cursor = myArray.size()
}
Cursor = Cursor +1
}
Pointer
]
val Functions$Function2<List<String>, String, Number> findCommand= [ myArray, command |
var Number Cursor = 0
var Boolean Commandfound = false
while(Cursor < myArray.size()) {
if(command == myArray.get(Cursor.intValue)) {
Commandfound = true
Cursor = myArray.size()
}
Cursor = Cursor +1
}
Commandfound
]
val Procedures.Procedure2<String, String> setUpdate = [myItem, myCommand |
var String Device
Device = "Sonos" + myItem
Device.sendCommand(myCommand)
]
rule "Sonos Multiroom"
when
Item SonosMultiroom received command
then
logInfo("SonosMultiroom.Service", "Multiroom Steuerung active")
var ArrayList<String> SonosCoordinatorList = newArrayList("empty")
var List<String> SonosControl
var List<String> SonosMode
var String SonosCommand
var Boolean AutoremoveServiceRunning = false
var Boolean JobSuccesfull = false
var Boolean MultiroomForced = false
var Boolean ResetCursor = false
var Number CursorPointer
var Number Cursor
var Number SonosControlCursor
var Number SonosModeCursor
JobSuccesfull = prerequiremets.apply("run")
if(MultiroomVolumeAvailable.size() < SonosPlayer.size()) {
MultiroomVolumeAvailable = initMultiroomVolumeAvailable.apply(SonosPlayer)
}
if(MultiroomVolumeOffset.size() < SonosPlayer.size()) {
MultiroomVolumeOffset = initMultiroomVolumeOffset.apply(SonosPlayer)
}
if(PlayerStatusUpdateState.size() < SonosPlayer.size()) {
PlayerStatusUpdateState = initPlayerStatusUpdateState.apply(SonosPlayer)
}
if(PlayerStatusUpdateLock.isLocked) {
AutoremoveServiceRunning = true
}
Cursor = 0
while(Cursor < SonosPlayer.size()) {
SonosCommand = "Sonos" + SonosPlayer.get((Cursor).intValue) + "_Coordinator"
val ImportCoordinator = gSonos.members.findFirst[ i | i.name == SonosCommand ]
SonosCoordinatorList.add(((Cursor).intValue), ImportCoordinator.state)
Cursor = Cursor+1
}
Cursor = 0
if(Cursor == 0) {
val List<String> ReadCommands = SonosMultiroom.state.toString.split("/Devices=")
SonosControl=ReadCommands.get(1).split("/")
SonosMode=ReadCommands.get(0).split("/")
}
SonosModeCursor = 0
while(SonosModeCursor < SonosMode.size()) {
//DefaultVolume Start
if(SonosMode.get((SonosModeCursor).intValue) == "DefaultVolume") {
var Number SetVolume
var Number ZoneOffset
SetVolume = DefaultSpeakerVolume.get(now.getHourOfDay)
SonosControlCursor = 0
JobSuccesfull = findCommand.apply(SonosMode, "MultiroomTarget")
if(JobSuccesfull == true) {
SonosControlCursor = 1
}
if(MultiroomVolumeLock.isLocked){
//Warte eine Sekunde falls MultiroomVolume derzeit aktiv ist
Thread::sleep(500)
}
logInfo("SonosMultiroom.Service", "MultiroomVolume Service locked.")
MultiroomVolumeLock.lock()
try {
MultiroomVolumeServiceRunning = true
if(MultiroomForced == false) {
while(SonosControlCursor < SonosControl.size()) {
Cursor = getPlayerID.apply(SonosPlayer, SonosControl.get(SonosControlCursor.intValue))
if(MultiroomVolumeAvailable.get((Cursor).intValue) == -1) {
logWarn("SonosMultiroom.Service", "MultiroomVolume für die Zone " + SonosPlayer.get(Cursor.intValue) + " ist deaktiviet, der Befehl wird ignoriert.")
} else {
ZoneOffset = ((SetVolume/100)*MultiroomVolumeOffset.get((Cursor).intValue)).intValue
if(MultiroomVolumeAvailable.get((Cursor).intValue) != ZoneOffset){
MultiroomVolumeAvailable.set(((Cursor).intValue), ZoneOffset)
SonosCommand = ZoneOffset.toString
logInfo("SonosMultiroom.Service", "DefaultVolume will be applied for Zone " + SonosControl.get((SonosControlCursor).intValue) + ".")
setUpdate.apply(SonosControl.get(SonosControlCursor.intValue) + "_Volume", SonosCommand)
}
}
SonosControlCursor = SonosControlCursor+1
}
}
if(MultiroomForced == true) {
Cursor = 0
while(Cursor < SonosPlayer.size()) {
if(MultiroomVolumeAvailable.get((Cursor).intValue) == -2) {
ZoneOffset = ((SetVolume/100)*MultiroomVolumeOffset.get((Cursor).intValue)).intValue
if(MultiroomVolumeAvailable.get((Cursor).intValue) != ZoneOffset) {
MultiroomVolumeAvailable.set(((Cursor).intValue), ZoneOffset)
SonosCommand = ZoneOffset.toString
logInfo("SonosMultiroom.Service", "DefaultVolume will be applied for Zone " + SonosPlayer.get((Cursor).intValue) + ". ")
setUpdate.apply(SonosPlayer.get(Cursor.intValue) + "_Volume", SonosCommand)
}
}
Cursor = Cursor+1
}
}
}
finally{
MultiroomVolumeLock.unlock()
}
}
//DefaultVolume Ende
//MultiroomVolume Start
if(SonosMode.get((SonosModeCursor).intValue) == "MultiroomVolume") {
SonosControlCursor = 0
JobSuccesfull = findCommand.apply(SonosMode, "MultiroomTarget")
if(JobSuccesfull == true) {
SonosControlCursor = 1
}
JobSuccesfull = false
if(MultiroomVolumeLock.isLocked){
Thread::sleep(500)
}
logInfo("SonosMultiroom.Service", "MultiroomVolume Service locked.")
MultiroomVolumeLock.lock()
try {
MultiroomVolumeServiceRunning = true
var Number ZoneCoordinatorOffset
var Number ZonePlayerOffset
while(SonosControlCursor < SonosControl.size()) {
CursorPointer = getPlayerID.apply(SonosPlayer, SonosControl.get(SonosControlCursor.intValue))
if(MultiroomVolumeAvailable.get((CursorPointer).intValue) < 0) {
logWarn("SonosMultiroom.Service", "MultiroomVolume für die Zone " + SonosPlayer.get(CursorPointer.intValue) + " ist deaktiviet, der Befehl wird ignoriert.")
}
ZonePlayerOffset = MultiroomVolumeOffset.get((CursorPointer).intValue)
Cursor = 0
var Number MultiroomCursorPointer
while(Cursor < SonosPlayer.size()) {
if(SonosCoordinatorList.get((CursorPointer).intValue) == SonosUUID.get((Cursor).intValue) && SonosPlayer.get(Cursor.intValue) != SonosControl.get(SonosControlCursor.intValue)) {
if(MultiroomVolumeAvailable.get((Cursor).intValue) >= 0) {
MultiroomCursorPointer = Cursor
ZoneCoordinatorOffset = MultiroomVolumeOffset.get((Cursor).intValue)
JobSuccesfull = true
Cursor = SonosPlayer.size()
}
}
Cursor = Cursor+1
}
if(JobSuccesfull == true) {
var Number mvolume
mvolume= MultiroomVolumeAvailable.get((MultiroomCursorPointer).intValue)
mvolume = ((mvolume/ZoneCoordinatorOffset)*ZonePlayerOffset).intValue
if(mvolume != MultiroomVolumeAvailable.get((CursorPointer).intValue) && MultiroomVolumeAvailable.get(CursorPointer.intValue) != -1) {
SonosCommand = mvolume.toString
logInfo("SonosMultiroom.Service", "MultiroomVolume sets volume of current zone player " + SonosControl.get((SonosControlCursor).intValue) + " equal to zone coordinator volume.")
setUpdate.apply(SonosControl.get(SonosControlCursor.intValue) + "_Volume", SonosCommand)
}
}
if(JobSuccesfull == false) {
if(MultiroomVolumeAvailable.get(CursorPointer.intValue) != -1) {
logInfo("SonosMultiroom.Service", "Multiroom Volume will switch to DefaultVolume, there is no player with active MultiroomVolume to set volume available.")
SonosMode.set(((SonosModeCursor).intValue), "DefaultVolume")
MultiroomVolumeAvailable.set((CursorPointer.intValue), -2)
MultiroomForced = true
ResetCursor = true
}
}
JobSuccesfull = false
SonosControlCursor = SonosControlCursor+1
}
}
finally{
MultiroomVolumeLock.unlock()
}
}
//MultiroomVolume Ende
//DisableMultiroomVolume Start
if(SonosMode.get((SonosModeCursor).intValue)=="DisableMultiroomVolume"){
SonosControlCursor = 0
while(SonosControlCursor < SonosControl.size()) {
Cursor = getPlayerID.apply(SonosPlayer, SonosControl.get(SonosControlCursor.intValue))
if(MultiroomVolumeAvailable.get(Cursor.intValue) != -1) {
logInfo("SonosMultiroom.Service", "MultiroomVolume will be disabled for Zone " + SonosPlayer.get(Cursor.intValue) + ".")
MultiroomVolumeAvailable.set((Cursor.intValue), -1)
}
SonosControlCursor = SonosControlCursor + 1
}
}
//DisableMultiroomVolume Ende
//EnableMultiroomVolume Start
if(SonosMode.get((SonosModeCursor).intValue)=="EnableMultiroomVolume"){
SonosControlCursor = 0
while(SonosControlCursor < SonosControl.size()) {
Cursor = getPlayerID.apply(SonosPlayer, SonosControl.get(SonosControlCursor.intValue))
if(MultiroomVolumeAvailable.get(Cursor.intValue) == -1) {
logInfo("SonosMultiroom.Service", "MultiroomVolume will be enabled for Zone " + SonosPlayer.get(Cursor.intValue) + ".")
SonosCommand = "Sonos" + SonosPlayer.get((Cursor).intValue) + "_Volume"
val ImportVolume = gSonosVolume.members.findFirst[ i | i.name == SonosCommand ]
MultiroomVolumeAvailable.set((Cursor.intValue), (Integer::parseInt(ImportVolume.state)))
}
SonosControlCursor = SonosControlCursor + 1
}
}
//EnableMultiroomVolume Ende
//Standalone Start
if(SonosMode.get((SonosModeCursor).intValue)=="Standalone"){
SonosControlCursor = 0
while(SonosControlCursor < SonosControl.size()) {
CursorPointer = getPlayerID.apply(SonosPlayer, SonosControl.get(SonosControlCursor.intValue))
Cursor = 0
while(Cursor < SonosPlayer.size()) {
if(SonosCoordinatorList.get((Cursor).intValue) == SonosCoordinatorList.get((CursorPointer).intValue) && CursorPointer != Cursor) {
logInfo("SonosMultiroom.Service", "Standalone removes the Zoneplayer " + SonosPlayer.get((Cursor).intValue) +".")
SonosCommand = SonosUUID.get((Cursor).intValue)
setUpdate.apply(SonosControl.get(SonosControlCursor) + "_Remove", SonosCommand)
SonosCoordinatorList.set(((Cursor).intValue), SonosUUID.get((Cursor).intValue))
}
Cursor = Cursor+1
}
SonosControlCursor = SonosControlCursor+1
}
}
//Standalone Ende
//MultiroomTarget Start:
if(SonosMode.get((SonosModeCursor).intValue)=="MultiroomTarget"){
CursorPointer = getPlayerID.apply(SonosPlayer, SonosControl.get(0))
Cursor = 0
SonosControlCursor = 0
JobSuccesfull = false
while(SonosControlCursor < SonosControl.size()) {
Cursor = getPlayerID.apply(SonosPlayer, SonosControl.get(SonosControlCursor.intValue))
if(SonosControl.get(SonosControlCursor.intValue) != SonosPlayer.get(Cursor.intValue)) {
logInfo("SonosMultiroom.Service", "MultiroomTarget add Zoneplayer " + SonosPlayer.get((Cursor).intValue) + " to Zone " + SonosControl.get(0) + ".")
SonosCoordinatorList.set(((Cursor).intValue), SonosCoordinatorList.get((CursorPointer).intValue))
setUpdate.apply(SonosControl.get(0) + "_Add", SonosUUID.get(Cursor.intValue))
}
SonosControlCursor = SonosControlCursor+1
}
}
//MultiroomTarget Ende
//Multiroom Start
if(SonosMode.get((SonosModeCursor).intValue) == "Multiroom") {
var Boolean UseActivePlayers = false
JobSuccesfull = findCommand.apply(SonosMode, "UseActivePlayer")
if(JobSuccesfull == true) {
logInfo("SonosMultiroom.Service", "Multiroom will modify active players if needed, because the option UseActivePlayer is used.")
UseActivePlayers = true
}
SonosControlCursor = 0
JobSuccesfull = false
var Boolean SpeakerActive
while(SonosControlCursor < SonosControl.size()) {
SpeakerActive = false
CursorPointer = getPlayerID.apply(SonosPlayer, SonosControl.get(SonosControlCursor.intValue))
if(PlayerStatusUpdateState.get(CursorPointer.intValue) == 0 && UseActivePlayers == false) {
SpeakerActive = true
}
if(SpeakerActive == false) {
Cursor = 0
while(Cursor < SonosPlayer.size()) {
if(PlayerStatusUpdateState.get(Cursor.intValue) == 0 && SonosPlayer.get((Cursor).intValue) != SonosControl.get((SonosControlCursor).intValue)) {
var Number MultiroomCursor = getPlayerID.apply(SonosUUID, SonosCoordinatorList.get(Cursor.intValue))
JobSuccesfull = true
logInfo("SonosMultiroom.Service", "Multiroom add " + SonosPlayer.get((CursorPointer).intValue) + " to Zone " + SonosPlayer.get((Cursor).intValue) + ".")
SonosCoordinatorList.set(((CursorPointer).intValue), SonosUUID.get((MultiroomCursor).intValue))
setUpdate.apply(SonosPlayer.get((MultiroomCursor).intValue) +"_Add", SonosUUID.get((CursorPointer).intValue))
Cursor = SonosPlayer.size()
}
Cursor = Cursor+1
}
}
SonosControlCursor = SonosControlCursor+1
}
if(JobSuccesfull == false && SpeakerActive == false) {
logInfo("SonosMultiroom.Service", "Multiroom will change to Play, because there is no active Zone available.")
SonosMode.set(((SonosModeCursor).intValue), "Play")
MultiroomForced = true
ResetCursor = true
}
}
//Multiroom Ende
//Play Start
if(SonosMode.get((SonosModeCursor).intValue) == "Play") {
JobSuccesfull = false
logInfo("SonosMultiroom.Service", "Play will play the Default URI in Zone " + SonosControl.get(0) + ".")
setUpdate.apply(SonosControl.get(0) + "_PlayURI", DefaultPlayUri)
JobSuccesfull = true
ResetCursor = false
JobSuccesfull = findCommand.apply(SonosMode, "MultiroomTarget")
if(JobSuccesfull == true) {
SonosMode.set(((SonosModeCursor).intValue), "MultiroomVolume")
MultiroomForced = true
ResetCursor = true
}
}
//Play Ende
//Remove Start
if(SonosMode.get((SonosModeCursor).intValue)=="Remove") {
SonosControlCursor = 0
JobSuccesfull = true
while(SonosControlCursor < SonosControl.size()) {
CursorPointer = getPlayerID.apply(SonosPlayer, SonosControl.get(SonosControlCursor.intValue))
Cursor = getPlayerID.apply(SonosUUID, SonosCoordinatorList.get(CursorPointer.intValue))
if(Cursor == CursorPointer) {
SonosCommand = "ON"
logInfo("SonosMultiroom.Service", "Remove Zoneplayer " + SonosControl.get((SonosControlCursor).intValue) + ".")
setUpdate.apply(SonosControl.get((SonosControlCursor).intValue) + "_StandAlone", SonosCommand)
SonosCoordinatorList.set(((Cursor).intValue), SonosUUID.get((Cursor).intValue))
} else{
SonosCommand = SonosUUID.get((CursorPointer).intValue)
logInfo("SonosMultiroom.Service", "Remove Zoneplayer " + SonosPlayer.get((CursorPointer).intValue) + ".")
setUpdate.apply(SonosPlayer.get((Cursor).intValue) + "_Remove", SonosCommand)
SonosCoordinatorList.set(((Cursor).intValue), SonosUUID.get((Cursor).intValue))
}
SonosControlCursor = SonosControlCursor+1
}
}
//Remove Ende
if(ResetCursor == true ) {
SonosModeCursor = SonosModeCursor-1
ResetCursor = false
}
SonosModeCursor = SonosModeCursor+1
}
if(AutoremoveServiceRunning == true) {
if(PlayerStatusUpdateLock.isLocked) {
Thread::sleep(2000)
if(PlayerStatusUpdateLock.isLocked) {
PlayerStatusUpdateLock.unlock()
}
}
}
if(MultiroomVolumeServiceRunning == true) {
MultiroomVolumeServiceRunning = false
if(MultiroomVolumeLock.isLocked()) {
MultiroomVolume.unlock()
}
logInfo("SonosMultiroom.Service", "MultiroomVolume Service unlocked.")
Cursor = 0
logInfo("SonosMultiroom.Service", "MultiroomVolume Service refreshes the volume index.")
while(Cursor < SonosPlayer.size()) {
if(MultiroomVolumeAvailable.get(Cursor.intValue) != -1) {
SonosCommand = "Sonos" + SonosPlayer.get((Cursor).intValue) + "_Volume"
val ImportVolume = gSonosVolume.members.findFirst[ i | i.name == SonosCommand ]
MultiroomVolumeAvailable.set(((Cursor).intValue), (Integer::parseInt(ImportVolume.state.toString)))
}
Cursor = Cursor+1
}
}
logInfo("SonosMultiroom.Service", "Multiroom Service inactiv.")
end