Looking for a way to allow command playSound to complete before continuing

Hello All!
I’ve been running OH 1.8 for many months and have really enjoyed expanding my system with the wealth of knowledge available on this forum. My system is on a RASPi 3 using MQTT with a mix of RFM69HCW packet radios with Arduino and ESP8266 NodeMcu’s.

I recently started using some rule-triggered audio responses for certain events and thought to utilize low power UHF radios to carry notifications. (I already use two-way radios on my property so it seemed to fit plus i once worked in the two-way repair industry).

I have linked the RASPi audio output with a radio’s audio input. I have a sendCommand to GPIO pin to “key” the radio before playSound starts the mp3 followed by another sendCommand to “dekey” to radio. Of course, as soon as playSound is executed, the next line is the Transmit OFF which only takes a split second.

I am looking for a way to delay the execution of the next line of code until playSound completes. Short of tweeking each rule using createTimer(now.plusSeconds) for each of the dozens of TTS generated mp3’s I’m using, I am stuck. I have looked at example rules and countless threads here but I can not see how to get past this.

Is there any returned value from playSound that I can utilize? I cant locate any evidence of this.

rule "driveway alarm"
        when
                Item driveway1 changed from OFF to ON
        then
                logInfo("LOG:","driveway rule start and voice is " + (VOICE.state.toString())
                if (VOICE.state == ON) {
                sendCommand(Transmit, ON)
                playSound("visitor.mp3")
                sendCommand(Transmit, OFF)
                logInfo("LOG:","driveway alert voice triggered")}
end

Any feedback would be appreciated.:relaxed:

Don’t know of any return value, but if you determine the sound duration, you could use Thread::sleep(<interval in ms>) after playSound() to delay the next command.

As @mstormi, Thread::sleep is a more appropriate solution than Timers in this instance.

But the challenge is calculating the length. If they were WAV files it would be a simple calculation to get at the length. But MP3s are compressed so their size is only slightly related to their playing length.

I spent about two minutes googling around and there appears to be a Python library that will calculate the length for you. You can implement a two or three liner Python script (python - time length of an mp3 file - Stack Overflow) which you call using val Number length = new Integer(executeCommandLine("/path/to/script /path/to/mp3", 5000)) and then do a Thread::sleep(length+1000) (add a little time just to keep it from being cut off too early if there is some latency in executing these commands).

I think it would be nice to get a “playout done” message from openHAB, this would be a new feature. So question is: is it possible to get this information to openHAB bus?

Probably not. I think actions like playSound completely bypass the event bus (its a major distinction between an Action and a Binding). But a new version of the playSound action could potentially be created that blocks until the sound is done being played.

Thanks Rick for the reply and thank you also @mstormi for your input.

Rick,
I checked the link you provided and and picked the mutagen python library. I WROTE MY FIRST PYTHON SCRIPT! AND IT WORKED! Between a book (that I never did complete) and some google searches, it returns the length from the executeCommandLine.

But something is not right.

The python code…

getlength.py

#!/usr/bin/python
import sys
file = str(sys.argv[1])  #takes the first argument path/to/mp3
from mutagen.mp3 import MP3
audio = MP3(file)
print "{:10.1f}".format(audio.info.length)  #returns the length in seconds  

The .1f format provides a value with only one number after the decimal. The original script would output 5 to 6 numbers after the decimal. I applied this hoping to rectify a problem I think is at the rule level.

rule "driveway alarm"
        when
                Item driveway1 changed from OFF to ON
        then
                logInfo("LOG:","driveway rule start and voice is " + (VOICE.state.toString()))
                if (VOICE.state == ON) {
                val Number length = new Integer(executeCommandLine("/usr/share/openhab/sounds/getlength.py /usr/share/openhab/sounds/doorbell.mp3", 5000))
                sendCommand(Transmit, ON)
                playSound("doorbell.mp3")
                Thread::sleep(length+1000)
                sendCommand(Transmit, OFF)
                logInfo("LOG:","file length is  " + (VOICE.state.toString()))
                logInfo("LOG:","driveway alert voice triggered")}
end

I do not see the sendCommand Transmit, ON nor either of the last two logInfo requests in the log file, so I am assuming its bailing out on the executeCommandLine.

2016-08-31 13:14:06.968 [INFO ] [org.openhab.model.script.LOG: ] - driveway rule start and voice is ON
2016-08-31 13:14:06.999 [DEBUG] [g.openhab.io.net.exec.ExecUtil] - executed commandLine '/usr/share/openhab/sounds/getlength.py /usr/share/openhab/sounds/doorbell.mp3'
2016-08-31 13:14:07.161 [DEBUG] [g.openhab.io.net.exec.ExecUtil] - exit code '0', result '       6.4'
2016-08-31 13:14:07.162 [ERROR] [o.o.c.s.ScriptExecutionThread ] - Error during the execution of rule 'driveway alarm': For input string: "       6.4"

I was able to look at the rule on designer on my Windows 7 laptop. (I really like OH Designer but its not the most stable program i have ever run, for shur:confounded: It will not launch again. Downloading the zip file again)
Designer indicated there was an issue with the Thread::sleep entry. I cant remember the exact syntax of the flag, but it led me to believe the length value from the executeCommandLIne might need to be converted??? Needed to be a Long or BigNumber???

I am sorting through the forum for any previous problems of this sort but there are not many instances where the length value from Thread::sleep is derived from the product of a prior function.

I’m thinking the line “val Number length = new Integer(executeCommandLine” needs to be “val Number length = new Long(executeCommandLIne…” but through the examples on this forum, I hoped to determine WHY this will not work instead of throwing different code in and crossing my fingers. No joy so far.

Thanks for your time.

You are trying to create an Integer using a value with a decimal. Try using new Float(execute...

Secondly, you need to convert the Number back to a primitive int so try:

Thread::sleep(length.intValue+1000)

That did it!!

Thanks so much for your help.

At first try, the GPIO pin only stayed high for a second until I realized the value of “length” of 6.4 was being added as only 6.4 milliseconds to the extra 1000 you recommended for processing time. Multiplying length by 1000 with additional 1000 in the Thread::sleep resulted in a 7 second delay.

rule "driveway alarm"
        when
                Item driveway1 changed from OFF to ON
        then
                logInfo("LOG:","driveway rule start and voice is " + (VOICE.state.toString()))
                if (VOICE.state == ON) {
                val Number length = new Float(executeCommandLine("/usr/share/openhab/sounds/getlength.py /usr/share/openhab/sounds/doorbell.mp3", 5000))
                sendCommand(Transmit, ON)
                playSound("doorbell.mp3")
                Thread::sleep((length.intValue*1000)+1000)
                sendCommand(Transmit, OFF)
                logInfo("LOG:","driveway alert voice triggered")}
end

pi@raspberrypi:~ $ cat /var/log/openhab/openhab.log |grep -i "gpio 4"
 2016-08-31 16:10:13.038 [DEBUG] [.o.b.gpio.internal.GPIOBinding] - Send command 1 to GPIO 4
 2016-08-31 16:10:20.062 [DEBUG] [.o.b.gpio.internal.GPIOBinding] - Send command 0 to GPIO 4

Thanks again for your assistance!

1 Like

I know this thread is old, but I am wondering if there are any changes in OH2+ (presently on 2.4, but can bump to testing) to determine whether playSound is done playing a sound. I can utilize a Thread::sleep (but it can be 30+ seconds), but ideally, I’d detect when it’s done to change the state of much of my audio output system. That way, I don’t have to script the routine for many variations of sounds that should be played under various circumstances that may be of different lengths. I figure that since OH has changed in the last 3 years, this situation may now be different.

There has been no change on this to my knowledge. But there is a DP and Scripted Automation library module that makes handling this easier and safer than using Thread::sleep. See Design Pattern: Gate Keeper.