Make rule play different sound each time doorbell is pressed

I have a rule set up to play a 90’s era rap hook when someone rings the doorbell. Straight forward enough, that in and of itself is working just fine at this point. I was wondering if there is a way to have it cycle through a list of audio files, playing a different one each time the door bell is pressed. The first time it would play file db01.mp3, the next time it was pressed it would play db02.mp3, the next time db03.mp3, and so on. After it has cycled through all the files (at this point up to db08.mp3) it would start back over with the db01.mp3

Here is the rule as it stands now

rule "Door Bell"
when
    Item DoorbellNotification changed from 0 to 1
then
    playSound("db01.mp3")
    
end

I have a hunch I’m going to have to incorporate something to do with persistence but I’m not sure. At this point I have MySQL up and running, as far as I know it only logs the status history of items. So I can see when the doorbell has been pressed, but not necessarily the last audio file that was played when it was pressed.

Any ideas? I’m running OpenHab2.2 on a RiPi3.

Not quite the same, but maybe it helps:
Below a rule that plays a random voice message out of a pool of messages every time it is triggered:

import java.util.concurrent.locks.ReentrantLock
import java.util.concurrent.TimeUnit
rule "VoiceMSG"
when
  Item gBedroom changed from ON to OFF
then
// logInfo("Notifications","Notifications rule got triggered")
var gotLock = lock.tryLock(1000, TimeUnit.MILLISECONDS)
	try {
		if (gotLock) {
		    	var Quotes = newArrayList(
						"string",
						"string",
						"string",
						"string",
						"string"
						)
		        var i = rand.nextInt(Quotes.size);
		        logInfo("VoiceCMD","number of quotes available: "+Quotes.size+"; reading out quote: " + i)
		        var msg = Quotes.get(i)
		        say(msg,"voicerss:enUS", "squeezebox:squeezeboxplayer:BridgeID:PlayerMAC")
		}		   
		else logInfo("VoiceMSG", "re-entry lock activated")
	}
	finally {
		if(gotLock){
			lock.unlock()
		}
	} 
end

Some notes:

  • I shortened it for readability, my original rule has more conditions (and the text is not only “string”) so apologies if I missed to delete some closing parenths
  • my rule is triggered by a group and such can get triggered multiple times (one time for each member), the re-entrant lock ensures that it is execute only once
  • for whatever reason, my system had trouble with getting a lock using the default setting of lock.tryLock; explicitly adding a time limit here, resolved that for me, this may not be necessary
  • arrays are indexed starting with zero, so the quote count displayed in the logs is off by one

Here’s my round-robin version. Essentially it’s the same as @lipp_markus’, key is to use an ArrayList.

It’s got nothing to do with persistence … except maybe that you need an item to store the current sound number and that you should persist that item so on reloads it’s not reset or even NULL which would lead to a (permanent) error in rule execution (that’s why I’m checking for NULL && eventually correcting this in my rule).

var String Silence = "silence-10sec.mp3"

if (es_klingelt)
         exit

es_klingelt = true

val int Anzahl_Klingeltoene = 7
val Klingeltoene = newArrayList('doorbell.mp3', 'Klingelton-GranVals-Variation.mp3', 'Pferd-aufm-Flur.mp3', 'SMS_Alarm.mp3','Hells_Bells.mp3', 'laughter.mp3', 'Was_wolln_Sie_denn_hier.mp3')

if (Klingelton.state == NULL)
        Klingelton.postUpdate(1)
var String s = Klingeltoene.get((Klingelton.state as Number).intValue)
var Number neuer_Klingelton = Klingelton.state

neuer_Klingelton = neuer_Klingelton + 1
if (neuer_Klingelton == Anzahl_Klingeltoene)   
        neuer_Klingelton = 0
Klingelton.postUpdate(neuer_Klingelton)

playSound (s)
playSound (Silence)

es_klingelt = false

My ‘lock’ is the es_klingelt variable . In another cron-triggered rule, I’m resetting es_klingelt to false every minute to ensure just a single sound is being played per minute.
If you don’t lock, with longish sound files and every postman pressing two or three times per default you could have overlapping sounds or sounds queueing up.

1 Like

And just to provide another approach which takes advantage of the fact that you have a consistent naming scheme for the mp3 files (note, the rule assumes you are starting from 0, not 1):

var currDBNum = -1
val numSounds = 8

rule "Door Bell"
when
    Item DoorbellNotification changed from 0 to 1
then
    currDBNum = (currDBNum + 1) % numSounds
    val sound = String.format("db%02d.mp3", currDBNum)
    playSound(sound)
end