Chromecast audiogroup has an "elected leader" and it keeps changing

Thank you for that - I can’t believe I didn’t think of doing that so much earlier!
I’ve modified mine for the 4 (at the moment) Google Homes around the house.

how is it?.. is it working as expected?

This is a smart solution, quickly tested it and it seems to work as expected (i’m on OH3). I am now going to understand how to solve my situation: I want to have a chromecast audio group as a default audio sink in openHAB, so to use say command to give alerts about specific events. Changing in the rule the default audio sink should be possible using something like:

say("Hello world!", "voicerss:enUS", audio_sink.state.toString)

@Mohammad_Chaaban, thanks for this solution. I have a rule that plays the radio on weekday mornings, and wanted to cast it to my audio group (instead of just the Chromecast in my bedroom). I don’t need to control the audio group from a sitemap, so I just added the status checks into my rules to ensure I cast to the elected leader.

is there a way to “lock” my audiogroup to have only 1 leader?If i manually create my audiogroup thing from a .thing file and give it the ip of the google home i want to be leader will it work?

No, because that doesn’t have any effect on the behaviour of the Chromecast devices. That just tells openHAB where to look.

I initially set up a rule to alert me when the elected leader changes and discovered that it will briefly change to one of the other two devices and then back to the original. I don’t know if this is the case for others.

There was one time when the port changed, and then I had to rediscover the audio group to get it going again, but that hasn’t happened again.

so i am still losing my chromecast group from time to time and the “quick” way to restore it is to delete the thing and readd it from inbox…Is there a way to automate this?If the group goes offline delete the thing and readd it with a rule of some kind…

I don’t think you can do that with a rule.

I found that after setting static IP addresses for my Chromecasts, openHAB doesn’t have an issue finding them. I suspect that when the leader changes, it’s due to one Chromecast rebooting for a software update. But after that happens, it always returns to being the leader.

Hi,
I have a group with 4 speakers. All fixed IPs. This rule below monitors the group status and if it goes OFFLINE it iterates over speaker IPs until the group changes to ONLINE again:

const { triggers, things } = require('openhab');

console.loggerName = 'org.openhab.custom.rules.chromecastrules';

const ipAddresses = ['10.200.0.13', '10.200.0.9', '10.200.0.11', '10.200.0.12'];
const groupID = 'chromecast:audiogroup:Notification';

var ipIndex = 0;

rules.JSRule({
    name: 'Notification Group OFFLINE',
    description: 'Notification group status monitoring.',
    triggers: [
        triggers.ThingStatusUpdateTrigger('chromecast:audiogroup:Notification', 'OFFLINE')
    ],
    execute: (event) => {
        if (ipIndex < ipAddresses.length && things.getThing('chromecast:audiogroup:Notification').statusInfo.includes('Connection refused')) {
            console.log('Nontification group offline. Changing its IP to ' + ipAddresses[ipIndex]);
            var thing = things.getThing(groupID);
            thing.setEnabled(false);
            thing.rawThing.getConfiguration().put('ipAddress', ipAddresses[ipIndex++]);
            thing.setEnabled(true);
            if (ipIndex === ipAddresses.length) {
                //give some time to OH before starting over
                setTimeout(() => {
                    ipIndex = 0;
                }, 10000);
            }
        }
    },
    tags: ['Notification'],
    id: 'NotificationGroupStatusMonitor'
});
2 Likes

Hi Gabor, I like you rule, but am not quite sure how to fully integrate/incorporate the above code into openhab rules (openhab 4.0). Could you perhaps elaborate a bit on how to do this?

Just create a js file (e.g. ChromecastRule.js) under /conf/automation/js with the code above. Don’t forget to edit your IPs in the array and set the group ID you used to set up your speaker group. OH4 will automatically parse it and create the rule for you.
BR

Thanks for your quick response Gabor. Just one follow-up question regarding your code.

So the basic process is that you disable the offline thing, update its ipaddress, and then enable it. If the thing goes back online than all is well. If the thing does not go back online, the rule will trigger again when the updated thing goes offline.

If the above is correct, then my question has to do with the settimeout section, and specifically how does retrigger the rule (with ipIndex=0) once the time is up?

For example, if none of the ipaddress will work, then what I see happening is that when the last ipaddress is reached the timer will be set, and when the thing with this last ipaddress inevitably goes offline line, the rule will be retrigger but no action will be taken since ipIndex < ipAddresses.length is not true.

Once the timer goes off, ipIndex is reset to zero, but I don’t see how the rule will be retriggered to start again cycling through the ipaddresses (since the think with the last ipaddress has presumably already gone offline).

Thanks in advance.

The binding will retry to re-establish the connection after some time. This will retrigger the cycle again. The worrying thing in your case is that based on your description you have a speaker group state without any leader in some cases. I haven’t experienced such case with my setup.

As I depend on Chomecast Groups, I fixed it with this “dirty” solution.
I have a textual thing configuration with all IPs and Ports I have found out to be working so far.
I have commented all out, except for one.
If a group goes offline, a rule is detecting it and starting a shell script. The shell script is moving the “unmarked” row one further. The rule is checking if the group is online now. If not, the script is started another time. And so on until all IP and Port combinations are tested.
It is running now for two weeks, rescued the online state already 12 times and it is working perfectly.

  • First of all you need a list of all IPs of every Chromecast device. This can be found out over the Google Home App. Choose every single device and note the IP. In my case: 192.168.176.45, 192.168.176.44, 192.168.176.42
  • After that you can check which Ports are used. This can change frequently. Every time a group goes offline, I deleted, scanned it afterwards and noted all ports. My actual list shows these ports as potential Chromecast Group Ports: 32105,32157,32222,32137,32088,32216,32217
  • You also need the ID of your group. In my case: chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b

So I made a list of identical audiogroups, but in all combinations of IPs and Ports.
Have a look (notice, only one row is not commented):

/etc/openhab/things/chromecastGroupHouse.thing

//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.45", port=32105, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.45", port=32157, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.45", port=32222, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.45", port=32137, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.45", port=32088, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.45", port=32216, refreshRate=100 ]
chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.45", port=32217, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.44", port=32105, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.44", port=32157, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.44", port=32222, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.44", port=32137, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.44", port=32088, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.44", port=32216, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.44", port=32217, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.46", port=32105, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.46", port=32157, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.46", port=32222, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.46", port=32137, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.46", port=32088, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.46", port=32216, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.46", port=32217, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.42", port=32105, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.42", port=32157, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.42", port=32222, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.42", port=32137, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.42", port=32088, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.42", port=32216, refreshRate=100 ]
//chromecast:audiogroup:19270866-2a17-40c9-bd83-f1151475662b "ThingChromecastGroupHouse"	[ ipAddress="192.168.176.42", port=32217, refreshRate=100 ]

The you need a shell script that will open the chromecastGroupHouse.thing file and change the comments for one row.

/etc/openhab/chromecastGroupHouse.sh (make sure the owner is openhabian:openhab and the permissions are set to 755)

#!/bin/bash

cd /etc/openhab/things

file="chromecastGroupHouse.things"
actualRowNumber=1
uncommentedRowFound=0
firstRowWithText=0;
while read line; do
	length=$(expr length "$line")
	
	if [[ "$length" -le 1 ]]; then
		noteRow="EmptyRow"
	else
	
		
		if [ "$firstRowWithText" -eq 0 ]; then
			echo "Set first row with text to $actualRowNumber"
			firstRowWithText=$actualRowNumber
		fi
		
		echo "# ${line}"


		firstTwoChars=$(echo "$line"| cut -c 1-2 )
		
		
		
		if [[ $firstTwoChars = "//" ]]; then
			noteRow="FoundComment"
			if [ "$uncommentedRowFound" -eq 1 ]; then
				noteRow="FoundCommentAndRemovedComment"
				sed -i "${actualRowNumber}s/^\/\///" "${file}"
				uncommentedRowFound=2
			fi
		else
			
			# Add comment chars
			noteRow="FoundNoComment"
			sed -i "${actualRowNumber}s/^/\/\//" "${file}"
			
			
			if [ "$uncommentedRowFound" -eq 0 ]; then
				noteRow="FoundNoCommentAndAddedComment"
				uncommentedRowFound=1
			fi
				
					
		fi
		
	
	fi
	actualRowNumber=$(($actualRowNumber+1))
	
	printf "row:$actualRowNumber noteRow:$noteRow length:$length\n"
	
done < "${file}"

printf "firstRowWithText:$firstRowWithText"

# if last row was uncommented, the next one would be the first row again
if [ "$uncommentedRowFound" -le 1 ]; then
	echo "Remove comment at the frist row"
	
	sed -i "${firstRowWithText}s/^\/\///" "${file}"
fi

You can run the script manually and check if the comment chars are moving.

bash /etc/openhab/chromecastGroupHouse.sh

After that I have added this rule (make sure the maxRows is set to your count of rows)

rule "rChromecastGroupHouse" when
	Thing "chromecast:audiogroup:9712431b-d890-40c8-bee4-424c65b5f1f4" changed from ONLINE
then
	val rn = "rChromecastGroupHouse"
	
	logInfo(dn,rn+" rule started" )
	
	val maxRows = 28
	val cmnd = "bash /etc/openhab/chromecastGroupHouse.sh"
	var countTries = 0;
	
	
	var thingStatus = getThingStatusInfo("chromecast:audiogroup:9712431b-d890-40c8-bee4-424c65b5f1f4")
	logInfo(dn,rn+" thingStatus:"+thingStatus)
	
	if( thingStatus !== null && thingStatus.toString.contains("ONLINE") ) {
		logInfo(dn,rn+"Group is already online.")
		return;
	}
	
	
	while ( countTries < maxRows) {
	
		executeCommandLine(Duration.ofSeconds(60),  cmnd.split(" "))
		
		Thread::sleep(2*1000) // 1000 = 1 Sekunde
		
		
		var thingStatus = getThingStatusInfo("chromecast:audiogroup:9712431b-d890-40c8-bee4-424c65b5f1f4")
		logInfo(dn,rn+" thingStatus:"+thingStatus)
		
		if( thingStatus !== null && thingStatus.toString.contains("ONLINE") ) {
			logInfo(dn,rn+"Group House online.")
			//sendBroadcastNotification("Chromecast Group House sucessfully defined.")
			
			return;
			
		}
		countTries=countTries+1
	}
	
	sendBroadcastNotification("Chromecast Group House could not be defined.")
	
end

Have fun.