[SOLVED] Get thing uid to use with say()

Hi all,

I’d like to use the say() command with all my google home devices.

What I would like to do is to use something like this:

say(TTS,“voicerss:deDE”,“chromecast:audiogroup:45beba5c-5205-40b9-90ee-31c9c6d49fa6”)

in a foreach loop:

gGoogleHome.members.forEach(…)

How do I retrieve the required thing uid?

Btw: First I created a google home audio group, but unfortunately this audio group is treated “offline” if one of the group members (one google home device) is powered off. Thus I’d like to use the say() command for each individual device.

best regards
Stefan

Show how you tried to create the Group. There might be something we can do there. You can only have a Group of Items as far as I’m aware so it isn’t clear what you’ve done.

Beyond that, I think your only option is to use the REST API to search for the relevant Things. Or you can hard code them into the Rule in an arrayList or the like.

There are other potential approaches but I’d need to know more about what you’ve actually tried.

1 Like

well, for now I’m only in design phase :wink:

I can just say that I have multiple things (google home devices) available. Each offers different channels, like setting the volume via: chromecast:chromecast:cc41b57679381809e5927107e4ceffd8:volume

This would end up in something like:

Group gGoogleHome
Dimmer Google_HomeOffice        (gGoogleHome)   { channel="chromecast:chromecast:5db9907619573b18167e4545b36db47f:volume" }
Dimmer Google_Kitchen           (gGoogleHome)   { channel="chromecast:chromecast:cc41b57679381809e5927107e4ceffd8:volume" }
Dimmer Google_Heimkino          (gGoogleHome)   { channel="chromecast:chromecast:d56cc2812251c4df993b0b59f4953128:volume" }
Dimmer Google_Wohnzimmer        (gGoogleHome)   { channel="chromecast:chromecast:1a878c7e0bad179784fd70253a8d80d9:volume" }

I’m using the say command in conjunction with a user-selectable AudioSink, although the selection possibilities are hard coded.

rule "SagEtwas"


when
  Item SayCommand received update  //user changeable string
then
  var String AudioSink
  switch AudioSink {
    case Lautsprecher.state.toString=="Küche" : AudioSink="sonos:PLAY1:PlayKueche" // Lautsprecher is a dropdown on HABPanel
    case Lautsprecher.state.toString=="Wohnzimmer" : AudioSink="sonos:PLAY1:PlayWohnzimmer"
    case Lautsprecher.state.toString=="Büro" : AudioSink= "sonos:PLAY1:PlayBuero"
    case Lautsprecher.state.toString=="Gästezimmer" : AudioSink= "sonos:PLAY1:PlayGaestezimmer"
    default:AudioSink="sonos:PLAY1:PlayWohnzimmer"
    }
  say(Command.state.toString,"voicerss:deDE",AudioSink)   
end

thanks a lot, but frankly speaking, I have no clue how this would help me :slight_smile: maybe I don’t see the forrest for the trees?

You do know that you can designate a Google Home speaker to be the “default speaker aka AUDIO SINK” for OpenHab by using the Chromecast binding?

To others - Yes I know “default speaker” is a bit broad, just trying to get the point across.

Squid

Well, this is what we refer to as the XY Problem.

You’ve asked about a technique to solve a problem without telling us the overall problem you are trying to solve. This robs us of critical information that we can use to provide the best help possible.

Anyway, opus’s example shows you a way to get a mapping between a human friendly name and a Channel name. Another way would be to create a Map file mapping Item names or a human friendly name to the channel name.

But since you haven’t told us why you want this mapping it’s really hard to help. opus, myself and everyone else will just be throwing out ideas, hoping one will stick.

1 Like

sorry for that. let me describe in more detail.
Using fritzbox binding I’d like my google home devices to tell me who’s calling my landline.
For this I read the callers name from the phonebook. This works like a charm.
Then I feed the callers name to the say() command. Works as well, but not always.

Why? In the google home app I’ve created an audiogroup (so this is NOT a group of openHAB items) but it constantly leads to problems when using in OH.Like here:

2019-03-26 03:46:44.698 [hingStatusInfoChangedEvent] - 'chromecast:audiogroup:45beba5c-5205-40b9-90ee-31c9c6d49fa6' changed from ONLINE to OFFLINE
2019-03-26 03:46:54.716 [hingStatusInfoChangedEvent] - 'chromecast:audiogroup:45beba5c-5205-40b9-90ee-31c9c6d49fa6' changed from OFFLINE to OFFLINE (COMMUNICATION_ERROR): Connection refused (Connection refused)
2019-03-26 03:51:32.279 [hingStatusInfoChangedEvent] - 'chromecast:audiogroup:45beba5c-5205-40b9-90ee-31c9c6d49fa6' changed from OFFLINE (COMMUNICATION_ERROR): Connection refused (Connection refused) to ONLINE

Thus I’d like to change this process and fire the say() command to each individual google home device.
For now I have established to raise/lower the volume whenever a call comes in. Like:

items:
`

>     Group gGoogleHome
>     Dimmer Google_HomeOffice        (gGoogleHome)   { channel="chromecast:chromecast:5db9907619573b18167e4545b36db47f:volume" }
>     Dimmer Google_Kitchen           (gGoogleHome)   { channel="chromecast:chromecast:cc41b57679381809e5927107e4ceffd8:volume" }
>     Dimmer Google_Heimkino          (gGoogleHome)   { channel="chromecast:chromecast:d56cc2812251c4df993b0b59f4953128:volume" }
>     Dimmer Google_Wohnzimmer        (gGoogleHome)   { channel="chromecast:chromecast:1a878c7e0bad179784fd70253a8d80d9:volume" }

rules:

gGoogleHome.members.forEach(GH|GH.sendCommand(100))

How can I use the forEach command to use say() with each device?

Stefan

You would need to create an ArrayList which contains all your Audiosinks (hardcoded!) and use the for-each technique for that list.
Using the say command that way will result in non synchronised messages played!

1 Like

Thanks: Btw: I tested this also with String items. This ways I hardcoded the thing UIDs inside the items file instead of rules file.

Then I tried using createTimer(now.plusSeconds(0) in each forEach loop, to achieve a more or less immediate output on all google home devices, unfortunately without success.

Stefan

There is too much going on at the same time in OH. You will never get the audio to play synchronized by sending an individual command to each device from OH.

You might be able to get close if you use JSR223 Jython Rules as the Jython is much more effecient, but I suspect your issue will be network and the binding instead of the Rules code. Also, keep in mind that there are only two threads to run Timers so even if your approach worked, it would only work for two of the Google Homes and the rest would be out of sync.

1 Like

alright, so it’s best to use the audiogroup. I’ve created an issue here:

So, you had a requirement of playing the message SYNCHRONISED. Knowing that, nobody would have suggested the for-each approach.

well, it’s nice to have, but not a must have. It the devices will play with let’s say 500ms gap, it’s fine. But actually it’s much much more

The problem is that exactly that is not predictable.
The (each) say command is executed asynchronically, within it, the TTS service is called and the thread has to wait for that ( request via internet with unpredictable speed or via localhost to the cache), the audiosink might need to stop a task or not , openHAB has a thread for this task available ATM or not,…

1 Like

My current solution to play a message / sound on ALL my google home devices is this:

  • say I have 4 google home devices.
  • I created one speaker group in google home app
  • I then created four audiogroup things in openhab. One for each of my google home device’s ip address like so:
Thing chromecast:audiogroup:gh_all_1         "All Google Home 1"       [ ipAddress="192.168.1.200", port=42351 ]
Thing chromecast:audiogroup:gh_all_2         "All Google Home 2"       [ ipAddress="192.168.1.203", port=42351 ]
Thing chromecast:audiogroup:gh_all_3         "All Google Home 3"       [ ipAddress="192.168.1.208", port=42351 ]
Thing chromecast:audiogroup:gh_all_4         "All Google Home 4"       [ ipAddress="192.168.1.165", port=42351 ]

Note that all these 4 GH devices belong to the same Speaker group in google home app.

  • Then in my code to play, I loop through each of those things, and see which one is online, and play it on the first one that is online. Sorry I’m using jython but you could probably do the same using rulesdsl
       for i in range(4): 
            chromecast_uid = "chromecast:audiogroup:gh_all_{}".format(i+1)
            if things.get(ThingUID(chromecast_uid)).status.toString() == "ONLINE":
                Audio.playSound(chromecast_uid, "defaultdoorbell.mp3")
                break

I do have one question, slightly off topic. I hope @rlkoshak or someone else could help. Note in my code above, I converted the things.get().status to string and comparing it to the string “ONLINE”. Is there a way to not convert it to string and compare it to just a constant ONLINE in a similar manner we do with item status e.g. items.SwitchItemName == ON ?

Instead of a String, compare to a ThingStatus.

Thanks, unfortunately I don’t know how to import that into jython

I figured it out. Currently it’s in eclipse, so my solution:

try:
    from org.openhab.core.thing import ThingUID, ThingStatus
except:
    from org.eclipse.smarthome.core.thing import ThingUID, ThingStatus

...
            if things.get(ThingUID(chromecast_uid)).status == ThingStatus.ONLINE:
1 Like

Updated method posted here: Chromecast audiogroup has an "elected leader" and it keeps changing