Passing multiple volume levels to a media player service call with multiple targets?

In NodeRED, I build a global variable which is an array of entities based upon whether or not a boolean is on for each entity.

The purpose is to be able to selectively mute each zone on my amplifier to prevent or allow any audio enabled entity to trigger a sound through that zone. If the boolean for a zone is off, it is removed from the array that is passed to the media_player service call.

When the grandfather clock (audio files) is triggered, the target for the media_player (6-zone amp) is an array of the zones that I want to be activated. I can choose in which rooms the clock chimes based upon this array passed to the service call node as the targeted entities.

What I can’t seem to do is add data to control each individual zone’s audio level with a single service call.

The input properties for a service call node need to be found under msg.payload. So, I load the global variable array into the payload on the fly and assign it to msg.payload.target.entity_id.

Here’s an example of what the message looks like on the input side:

{
    "domain": "homeassistant",
    "service": "play_media",
    "target": {
        "entity_id": ["media_player.zone_11", "media_player.zone_12"]
    }
    "data": {
       ????
    }
}

Now in the example in NodeRED, it shows that we can turn on multiple lights with one service call all to the brightness of 50%.

But, what if we want light 1 to be 25% and light 2 to be 60%? Is it possible to achieve, or will I need to come up with another routine to control the volume for each zone?

You probably need to iterate through the list in a function node and then call the service calls from there.
I have a similar setup with a list I create several sensors from.
The service node looks like this.

for (var i=0;i<msg.payload.stateList.device.length;i++) {
    var newmsg = {
        entity_id: "binary_sensor." + msg.payload.stateList.device[i].$.name.toLowerCase().replaceAll(" ", "_") + "_unreach",
        payload: {
            data: {
                state: msg.payload.stateList.device[i].$.unreach,
                attributes: {
                    name: msg.payload.stateList.device[i].$.name.toLowerCase().replaceAll(" ", "_") + "_unreach",
                    address: msg.payload.stateList.device[i].channel[0].datapoint[0].$.name.substring(8,24),
                    friendly_name: msg.payload.stateList.device[i].$.name + " Unreach",
                    last_update: msg.payload.stateList.device[i].channel[0].datapoint[0].$.timestamp,
                    icon: "mdi:access-point-remove"
                }
            }
        }
    };
    node.send(newmsg);
}

Hmm…okay, so first I use an inject node to trigger a “get entities” node which outputs an array of all “^input_boolean.zone_” entities to msg.payload.

Immediately after that I have a function node that splits the array into three new arrays. Each array represents an automation sound event. I have a grandfather clock, a motion sensor on the front patio, a doorbell, and test to speech. All of them do different things, but they can all be turned on or off independently for each zone on the amp.

It results in this:

{
  "motion": [
    "media_player.zone_12",
    "media_player.zone_13",
    "media_player.zone_14"
  ],
  "clock": [
    "media_player.zone_11",
    "media_player.zone_12",
    "media_player.zone_14"
  ],
  "doorbell": [],
  "tts": []
}

As you can see, nothing is active for tts or doorbell.

I also have a trigger state node that watches all of these booleans in the event that one is updated so the array will also be updated.

When I get to the point where I need to play the sound, I’ve consolidated all of the flows to build a payload with all of the information needed to utilize a single service call node and reduce the spaghetti on my screen.

The first service call turns on the zones. The 2nd clears the playlist on the PiCore Player, the 3rd plays the sound on the PiCore Player. I keep the PiCore Player volume constant and use the Zone amp levels to customize each zone volume.

It seems what I may need to do is first turn on the respective zones using a single service call with the array with the target entities, then iterate through the values: for each item, set the volume for that item equal to the volume on that item’s input number entity, which would be stored as a key/pair rather than an array, firing off the values quickly before sending the original message to the play-media service call for the PiCore.

Does this sound about right?

I think we must see the flow to help further, so try to export it to clipboard in Node Red and paste the text in here.

One thing I can suggest is to maybe split up your flow a bit.
First part is to make a flow that will keep the result updated on the result you post and then save that result in a flow variable. This till mean you can have trigger nodes for all the sensors that can change it and it will then just stay updated at all times ready for when you might need it.

When you need it you just read the flow variable and go on with your service calls.

You set a flow variable with flow.set("name_of_flow_variable",name_of_actual_variable), like flow.set('result_list", msg.payload)
You retrieve a flow variable with actual_variable = flow.get("name_of_flow_variable"), like msg.result = flow.get('result_list") or var result = flow.get('result_list")