Calling all Javascript Wizards: Need Help with Random Numbers for Volumio Playlists

The QUESTION is at the bottom of this if you don’t need all of this extra detail.

Using a trigger node, when a ^person.* triggers their location to home, I use a function to strip the text person. from the entity_id in the msg and pass that along:

var str = msg.data.entity_id
var person = str.replace("person.","");
flow.set('sfx_name', person)

var name = flow.get('sfx_name');
msg.payload = name;
return msg;

Using the simple queue node, each message stacks up to accommodate the potential arrival of two or more people simultaneously. To release those messages one at a time, a change node sends a trigger property to the input of the simple queue node, but only if the media player is sitting idle, otherwise the message waits until that’s the case.

When the message is released from the queue, the next function node defines a source for the media player (the play list in Volumio):

const gHomeAssistant = global.get('homeassistant').homeAssistant;
var sfx_holiday = gHomeAssistant.states['calendar.holidays_in_united_states'].attributes.message;

if (gHomeAssistant.states['calendar.holidays_in_united_states'].state == "on") {
    const randomIndex = Math.floor(Math.random() * 9);
    fileNumber = randomIndex + 10;
    msg.payload = sfx_holiday + fileNumber;
} else {
    msg.payload = "arrival_" + msg.payload;
}
return msg;

So the above function checks to see if today is a holiday. If so, we store the name of the holiday plus a random file number so corresponding playlists can be loaded. Today would produce 9 possibilities from Halloween10 to Halloween19.

If it’s NOT a holiday, we keep the original payload, which is the person’s name, i.e., firstname_lastname.

Note: The number of random options will change from a number to a payload that counts the number of available files in the hassio www folder to avoid extra update work when new playlists are added.

QUESTION(S):

If I have 9 playlists as indicated in randomIndex in the function example above, how would I modify the code to eliminate numbers as they’re used until all have been used, then reset and start over again so nothing is repeated until all have been used? Right now it’s truly random, but with such a small number, repeats happen frequently.

After this is accomplished, I would assume that a restart of HASS.io or even re-deploying after a change in NodeRED would reset the process…is this correct?

I normally would go the route of copying the playlist to an array and popping the selected element from that array.

Here’s an example for sort of what you were asking for:

[{"id":"54151598.832fac","type":"inject","z":"ffbd7f06.4a014","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":348,"y":1440,"wires":[["a1636746.936ec8"]]},{"id":"566c1ae7.194ff4","type":"debug","z":"ffbd7f06.4a014","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":630,"y":1440,"wires":[]},{"id":"a1636746.936ec8","type":"function","z":"ffbd7f06.4a014","name":"","func":"const arr = context.get(\"savedArray\") || [...Array(10).keys()];\n\nconst randomIndex = Math.floor(Math.random() * arr.length);\nmsg.payload = arr.splice(randomIndex, 1)[0];\ncontext.set(\"savedArray\", arr.length === 0 ? undefined : arr);\n\nreturn msg;","outputs":1,"noerr":0,"x":480,"y":1440,"wires":[["566c1ae7.194ff4"]]}]

I might need a little help understanding this code.

I’m not sure what this does: ? undefined : arr); and also this:

context.set("savedArray", arr.length === 0 ? undefined : arr);

Most importantly, the “arr.length === 0” part and beyond. Thanks.

That’s the ternary operator. Is a one line “if then else” sort of.

That’s saving the array into context(a variable), not flow or global but node context

I cannot look at the whole flow atm but length is a property of the array, how many elements are in there.

You can practice all these in the chrome console so you have a better understanding