whenever someone calls me on my landline, my google home devices tell me who is calling. This is done with a rule which uses a few say() commands. All devices will raise their voice sequentially (one after another). Is there any way to let them say() something at the same time? like running each command in it’s own thread?
rule "Phone is ringing"
when
// fboxRinging is a switch item which switches to ON if call is detected
Item fboxRinging changed from OFF to ON
then
// fboxIncoming call receives numbers/name of incoming call
var incCall = fboxIncomingCall.state as StringListType
var Account = incCall.getValue(0)
var callerNumber = incCall.getValue(1)
var incCallResolved = fboxIncomingCallResolved.state as StringListType
var callerName = incCallResolved.getValue(1)
logInfo("Incoming call", Account + " " + callerNumber + " / " + callerName)
//var GoogleHomeVol_WZ = GoogleHome_Volume_WZ.state
//var GoogleHomeVol_HK = GoogleHome_Volume_HK.state
sendCommand(GoogleHome_Volume_WZ, 100)
sendCommand(GoogleHome_Volume_HK, 100)
if(callerName.contains("not found"))
{
logInfo("Incoming call", "Unbekannt")
say("Unbekannter Anrufer","voicerss:deDE","chromecast:chromecast:1a878c7e0bad179784fd70253a8d80d9")
say("Unbekannter Anrufer","voicerss:deDE","chromecast:chromecast:d56cc2812251c4df993b0b59f4953128")
}else{
var callerNameStripped = callerName.split("\\(").get(0)
logInfo("Incoming call", callerNameStripped)
say(callerNameStripped,"voicerss:deDE","chromecast:chromecast:1a878c7e0bad179784fd70253a8d80d9")
say(callerNameStripped,"voicerss:deDE","chromecast:chromecast:d56cc2812251c4df993b0b59f4953128")
}
Thread::sleep(7000)
sendCommand(GoogleHome_Volume_WZ, 40)
sendCommand(GoogleHome_Volume_HK, 40)
end
You are saying that the two say command are executed sequentially one after another or does the second one starts before the first one has stopped, like there is a delay between the two?
I have an idea but it is a bit of a workaround
The idea is to create two rules in which you say something in each
Each rule will run it’s own thread
Create two switch items
Switch sayChromeCast1
Switch sayChromeCast2
Move the callerNameStripped variable at the top of your rule file so it become global in the file:
var callerNameStripped = ""
rule "Phone is ringing"
when
// fboxRinging is a switch item which switches to ON if call is detected
Item fboxRinging changed from OFF to ON
then
// fboxIncoming call receives numbers/name of incoming call
var incCall = fboxIncomingCall.state as StringListType
var Account = incCall.getValue(0)
var callerNumber = incCall.getValue(1)
var incCallResolved = fboxIncomingCallResolved.state as StringListType
var callerName = incCallResolved.getValue(1)
logInfo("Incoming call", Account + " " + callerNumber + " / " + callerName)
//var GoogleHomeVol_WZ = GoogleHome_Volume_WZ.state
//var GoogleHomeVol_HK = GoogleHome_Volume_HK.state
sendCommand(GoogleHome_Volume_WZ, 100)
sendCommand(GoogleHome_Volume_HK, 100)
if(callerName.contains("not found"))
{
callerNameStripped = "Unbekannter Anrufer"
logInfo("Incoming call", "Unbekannt")
sayChromeCast1.sendCommand(ON)
sayChromeCast2.sendCommand(ON)
//say("Unbekannter Anrufer","voicerss:deDE","chromecast:chromecast:1a878c7e0bad179784fd70253a8d80d9")
//say("Unbekannter Anrufer","voicerss:deDE","chromecast:chromecast:d56cc2812251c4df993b0b59f4953128")
}else{
callerNameStripped = callerName.split("\\(").get(0)
logInfo("Incoming call", callerNameStripped)
sayChromeCast1.sendCommand(ON)
sayChromeCast2.sendCommand(ON)
//say(callerNameStripped,"voicerss:deDE","chromecast:chromecast:1a878c7e0bad179784fd70253a8d80d9")
//say(callerNameStripped,"voicerss:deDE","chromecast:chromecast:d56cc2812251c4df993b0b59f4953128")
}
Thread::sleep(7000)
sendCommand(GoogleHome_Volume_WZ, 40)
sendCommand(GoogleHome_Volume_HK, 40)
end
rule "say ChromeCast 1"
when
Item sayChromeCast1 received command ON
then
say(callerNameStripped,"voicerss:deDE","chromecast:chromecast:1a878c7e0bad179784fd70253a8d80d9")
end
rule "say ChromeCast 2"
when
Item sayChromeCast2 received command ON
then
say(callerNameStripped,"voicerss:deDE","chromecast:chromecast:d56cc2812251c4df993b0b59f4953128")
end
This is really bad in Terms of perfomance and non intended side effects. In short: use the thread::sleep only shorter than a second, max should never reach more than a few seconds - because it simply blocks the few threads, openHAB has reserved. 7seconds is really long in terms of a CPU.
my approach would be something like a state machine there’s two tutorials on this one:
simple put: they will monitor a state and react on that. In your case I would add a proxy item, which indicates, that you didn’t pick up the phone yet and it has two or three states like the first 5 seconds, the second 5 seconds and the last 5 seconds. for each state change you just add more volume and can also have a different say-command.
Thomas, maybe I can remove the sleep completely. I added it because I don’t know how long the say() command will run. Once it is finished I lower the volume of both chromecast devices. Now I noticed that it seems this won’t run in another thread, so the execution of the rule will wait anyway for say() to come to an end.
rule "Phone is ringing"
when
// fboxRinging is a switch item which switches to ON if call is detected
Item fboxRinging changed from OFF to ON
then
// fboxIncoming call receives numbers/name of incoming call
var incCall = fboxIncomingCall.state as StringListType
var Account = incCall.getValue(0)
var callerNumber = incCall.getValue(1)
var incCallResolved = fboxIncomingCallResolved.state as StringListType
var callerName = incCallResolved.getValue(1)
logInfo("Incoming call", Account + " " + callerNumber + " / " + callerName)
//var GoogleHomeVol_WZ = GoogleHome_Volume_WZ.state
//var GoogleHomeVol_HK = GoogleHome_Volume_HK.state
GoogleHome_Volume_WZ.sendCommand(100)
GoogleHome_Volume_HK.sendCommand(100)
ChromeCastTimer.sendCommand(ON)
if(callerName.contains("not found"))
{
callerNameStripped = "Unbekannter Anrufer"
logInfo("Incoming call", "Unbekannt")
sayChromeCast1.sendCommand(ON)
sayChromeCast2.sendCommand(ON)
}else{
callerNameStripped = callerName.split("\\(").get(0)
logInfo("Incoming call", callerNameStripped)
sayChromeCast1.sendCommand(ON)
sayChromeCast2.sendCommand(ON)
}
end
rule "ChromeCast volume down after call"
when
Item ChromeCastTimer received command OFF
then
GoogleHome_Volume_WZ.sendCommand(40)
GoogleHome_Volume_HK.sendCommand(40)
end
rule "Phone is ringing"
when
// fboxRinging is a switch item which switches to ON if call is detected
Item fboxRinging changed from OFF to ON
then
// fboxIncoming call receives numbers/name of incoming call
var incCall = fboxIncomingCall.state as StringListType
var Account = incCall.getValue(0)
var callerNumber = incCall.getValue(1)
var incCallResolved = fboxIncomingCallResolved.state as StringListType
var callerName = incCallResolved.getValue(1)
logInfo("Incoming call", Account + " " + callerNumber + " / " + callerName)
//var GoogleHomeVol_WZ = GoogleHome_Volume_WZ.state
//var GoogleHomeVol_HK = GoogleHome_Volume_HK.state
GoogleHome_Volume_WZ.sendCommand(100)
GoogleHome_Volume_HK.sendCommand(100)
ChromeCastTimer.sendCommand(ON)
if (callerName.contains("not found")) {
callerNameStripped = "Unbekannter Anrufer"
logInfo("Incoming call", "Unbekannt")
} else {
callerNameStripped = callerName.split("\\(").get(0)
logInfo("Incoming call", callerNameStripped)
}
sayChromeCast1.sendCommand(ON)
sayChromeCast2.sendCommand(ON)
end
Please note the use on the item method item.sendCommand instead of the action `sendCommand(String, String). It is recommended to use the item method when the item is known (Which is the case here). The Action can be used occasionally for example when the item name is generated in a rule.
One idea to make the say ChromeCast rules generic.
Put the mapping between the Item name sayChromeCast1 and sayChromeCast2 and the address String in a .map file. Then the rules become
rule "Say ChromeCast"
when
Item sayChromeCast1 received command ON or
Item sayChromeCast2 received command ON
then
val address = transform("MAP", "chromecast.map", triggeringItem.name)
say(callerNameStripped, "voicerss:deDE", address)
end
When 2.3 is released we can replace the triggers with a Member of ChromeCastGroup.
@rlkoshak
The 2 different rule were to create 2 threads.
Will your mapping solution or even Member of also create 2 threads for “simultaneous” sound output?
Makes no difference, because the rule is triggered twice, with different triggeringItem.
But you have to keep in mind, that openHAB will per default only execute up to 5 rules in parallel threads. So, this will work for 2 players, but not for more than 4 players, (original rule plus 4 “say” threads)