jensrossbach / node-red-contrib-sony-audio

Node-RED nodes for accessing Sony Audio Control API
MIT License
2 stars 0 forks source link

Control over individual speaker volume via undocumented function. #30

Open pmonck opened 3 years ago

pmonck commented 3 years ago

I would like to be able to adjust the volume of individual speakers on my STR-DN1080, e.g. turn up the center to make dialog clearer or turn down the sub at night.

This appears to require the use of an undocumented API function "getSpeakerSettings"/"setSpeakerSettings" as mentioned in the Sony Developer Forum here: https://forum.developer.sony.com/topic/74/undocumented-features-of-the-sony-audio-control-api

Would it be possible to add this to the list of supported functions?

jensrossbach commented 3 years ago

Hi @pmonck, unfortunately the information from the Sony Developer Forum is a bit vague and I don't have the Sony STR-DN1080 in order to try out this API and find out more. Therefore I would appreciate if you could help me and find out more details of this API by making use of the low-level requests feature of the controller node.

I.e.,

Thanks!

pmonck commented 3 years ago

I'm very new to all this, but I'll try!

pmonck commented 3 years ago

I queried the available settings using information from the Bravia TV API here: https://pro-bravia.sony.net/develop/integrate/rest-api/spec/service/audio/v1_0/getSpeakerSettings/

This is what I got from my STR-DN1080. Does it help? { "service": "audio", "method": "getSpeakerSettings", "version": "1.0", "payload": [{ "candidate": [{ "isAvailable": true, "title": "Front & Center", "value": "front&center" }, { "isAvailable": true, "title": "Front", "value": "front" }, { "isAvailable": true, "title": "Off", "value": "off" }, { "isAvailable": false, "title": "", "value": "" }], "currentValue": "off", "isAvailable": true, "target": "inCeilingSpeakerMode", "title": "In-Ceiling Speaker Mode", "titleTextID": "sound-inceiling", "type": "enumTarget" }, { "candidate": [{ "isAvailable": true, "title": "Speaker A", "titleTextID": "speaker-speakers-a", "value": "speakerA" }, { "isAvailable": false, "title": "Speaker B", "titleTextID": "speaker-speakers-b", "value": "speakerB" }, { "isAvailable": false, "title": "Speaker A + B", "titleTextID": "speaker-speakers-aplusb", "value": "speakerA_B" }, { "isAvailable": true, "title": "Off", "titleTextID": "speaker-speakers-off", "value": "off" }, { "isAvailable": false, "title": "", "titleTextID": "", "value": "" }], "currentValue": "speakerA", "isAvailable": true, "target": "speakerSelection", "title": "Speakers", "titleTextID": "speaker-speakers", "type": "enumTarget" }, { "candidate": [{ "isAvailable": true, "max": 10, "min": -10, "step": 0.5, "title": "Front L", "titleTextID": "frontLLevel", "value": "0.5" }], "currentValue": "0.5", "deviceUIInfo": "picker", "isAvailable": true, "target": "frontLLevel", "title": "Front L", "titleTextID": "speaker-speakerlevel-frontl", "type": "doubleNumberTarget" }, { "candidate": [{ "isAvailable": true, "max": 10, "min": -10, "step": 0.5, "title": "Front R", "titleTextID": "frontRLevel", "value": "0.5" }], "currentValue": "0.5", "deviceUIInfo": "picker", "isAvailable": true, "target": "frontRLevel", "title": "Front R", "titleTextID": "speaker-speakerlevel-frontr", "type": "doubleNumberTarget" }, { "candidate": [{ "isAvailable": true, "max": 10, "min": -10, "step": 0.5, "title": "Center", "titleTextID": "centerLevel", "value": "7.0" }], "currentValue": "7.0", "deviceUIInfo": "picker", "isAvailable": true, "target": "centerLevel", "title": "Center", "titleTextID": "speaker-speakerlevel-center", "type": "doubleNumberTarget" }, { "candidate": [{ "isAvailable": true, "max": 10, "min": -10, "step": 0.5, "title": "Surround L", "titleTextID": "surroundLLevel", "value": "0.0" }], "currentValue": "0.0", "deviceUIInfo": "picker", "isAvailable": true, "target": "surroundLLevel", "title": "Surround L", "titleTextID": "speaker-speakerlevel-surroundl", "type": "doubleNumberTarget" }, { "candidate": [{ "isAvailable": true, "max": 10, "min": -10, "step": 0.5, "title": "Surround R", "titleTextID": "surroundRLevel", "value": "3.0" }], "currentValue": "3.0", "deviceUIInfo": "picker", "isAvailable": true, "target": "surroundRLevel", "title": "Surround R", "titleTextID": "speaker-speakerlevel-surroundr", "type": "doubleNumberTarget" }, { "candidate": [{ "isAvailable": true, "max": 10, "min": -10, "step": 0.5, "title": "Sur Back", "titleTextID": "surroundcBackLevel", "value": "0.0" }], "currentValue": "0.0", "deviceUIInfo": "picker", "isAvailable": false, "target": "surroundcBackLevel", "title": "Sur Back", "titleTextID": "speaker-speakerlevel-surroundback", "type": "doubleNumberTarget" }, { "candidate": [{ "isAvailable": true, "max": 10, "min": -10, "step": 0.5, "title": "Sur Back L", "titleTextID": "surroundBackLLevel", "value": "0.0" }], "currentValue": "0.0", "deviceUIInfo": "picker", "isAvailable": false, "target": "surroundBackLLevel", "title": "Sur Back L", "titleTextID": "speaker-speakerlevel-surroundbackl", "type": "doubleNumberTarget" }, { "candidate": [{ "isAvailable": true, "max": 10, "min": -10, "step": 0.5, "title": "Sur Back R", "titleTextID": "surroundBackRLevel", "value": "0.0" }], "currentValue": "0.0", "deviceUIInfo": "picker", "isAvailable": false, "target": "surroundBackRLevel", "title": "Sur Back R", "titleTextID": "speaker-speakerlevel-surroundbackr", "type": "doubleNumberTarget" }, { "candidate": [{ "isAvailable": true, "max": 10, "min": -10, "step": 0.5, "title": "Height L", "titleTextID": "heightLLevel", "value": "-2.5" }], "currentValue": "-2.5", "deviceUIInfo": "picker", "isAvailable": true, "target": "heightLLevel", "title": "Height L", "titleTextID": "speaker-speakerlevel-heightl", "type": "doubleNumberTarget" }, { "candidate": [{ "isAvailable": true, "max": 10, "min": -10, "step": 0.5, "title": "Height R", "titleTextID": "heightRLevel", "value": "0.0" }], "currentValue": "0.0", "deviceUIInfo": "picker", "isAvailable": true, "target": "heightRLevel", "title": "Height R", "titleTextID": "speaker-speakerlevel-heightr", "type": "doubleNumberTarget" }, { "candidate": [{ "isAvailable": true, "max": 10, "min": -10, "step": 0.5, "title": "Subwoofer", "titleTextID": "subwooferLevel", "value": "-7.5" }], "currentValue": "-7.5", "deviceUIInfo": "picker", "isAvailable": true, "target": "subwooferLevel", "title": "Subwoofer", "titleTextID": "speaker-speakerlevel-subwoofer", "type": "doubleNumberTarget" }], "_msgid": "ee22eb39.b958d8" }

jensrossbach commented 3 years ago

This sounds good, I'll have a look into this.

jensrossbach commented 3 years ago

@pmonck I have implemented the feature with the new release 1.7.0. As I cannot test it on my own, I see this currently as an experimental feature and I would appreciate if you could give me feedback if everything works as expected. Thanks!

pmonck commented 3 years ago

@jensrossbach Thanks for this!

Preliminary tests show that I can read and set individual speaker volumes on my STR-DN1080. This is exactly what I needed.

I have a further suggestion: Seeing as getSpeakerSettings/setSpeakerSettings apply to various different Sony devices and not just the STR-DN1080, it would be good to have a way to query the device for its list of available speakers. The user could then use the returned speaker names to control individual speakers on the specific receiver model they are using and not just the STR-DN1080.

Furthermore, if I use the "Get Speaker Settings" command with the setting "All" it returns all of the volume levels, but not the names of the respective speakers. It looks like the user would have to know which is which. It would be good to get the speaker names too.

The JSON I posted above seems to have all the required information. It would be good to be able to access this without the need for low-level override.

What do you think?

jensrossbach commented 3 years ago

Thanks for the feedback. Indeed I implemented the filter for Get Speaker Settings in a similar way as for Get Sound Settings and Get Playback Settings, i.e., I only expose the values. Of course I can extend this, so that also the speaker names are exposed. For now, you can use the raw output to get this information in an unprocessed way.

In my opinion, it would be sufficient to add the speaker names to the filter output of Get Speaker Settings in order to get a list of speakers. What do you think?

pmonck commented 3 years ago

In my opinion, it would be sufficient to add the speaker names to the filter output of Get Speaker Settings in order to get a list of speakers. What do you think?

Yes, I agree. If you give us all the relevant information and we can filter it as required.

At the moment the settings drop-down is specific to the STR-DN1080, so speakers present on other systems might be absent from the list. I'm not sure what might be the best way around this. Perhaps add speaker configuration to the device properties somewhere?

jensrossbach commented 3 years ago

At the moment the settings drop-down is specific to the STR-DN1080, so speakers present on other systems might be absent from the list. I'm not sure what might be the best way around this. Perhaps add speaker configuration to the device properties somewhere?

Yes, I assumed this. One option would be a speaker configuration as you propose, this would be a bit of effort to implement. Another option could be to add a special option with a text field, where the use can enter the speaker name manually. Or as third option, I can extend this list as soon as people come with other devices and their speakers. Maybe the speakers are not so different for the different devices.

Anyway, for programmatic usage, everything should be ready by using the getSpeakerSettings/setSpeakerSettings high-level or low-level commands.

jensrossbach commented 3 years ago

For the filter, I will implement it in this way: In case of a single result, I'll keep the same format as it is now, because this means that the value of a specific speaker was requested and there is no need to output the speaker name. If there are multiple results, I'll create an object with the speaker names as keys and the corresponding values assigned.

For the drop down list in the settings, I will think about another solution to query the speakers from the device and dynamically fill the menu with appropriate data. But this might take a bit longer, maybe I'll split those two topics and create separate releases.

jensrossbach commented 3 years ago

First point of above is now realized with version 1.7.1. For the other point I need more research and this will take some time.

pmonck commented 3 years ago

Works great. Thanks!

pmonck commented 3 years ago

Apologies if this is a basic question, but could you tell me how I use the new Set Speaker Settings command to set a speaker level to the value of an input variable rather than an absolute value selected in the settings box?

jensrossbach commented 3 years ago

No problem! You can do this by using the command override functionality of the controller node via the input message. This is described in more detail 👉🏼 here in the wiki. You can override the command including its parameters (in this case you can use a single controller node for different commands) or only the parameters and use the command as configured in the settings of the node.

For example, if you want to set speaker front left and subwoofer levels, you can do it like in the following JSON representation of the input message:

{
    "command": "setSpeakerSettings",
    "payload:":
    {
        "settings":
        [
            {
                "target": "frontLLevel",
                "value": 2.5
            },
            {
                "target": "subwooferLevel",
                "value": 6
            }
        ]
    }
}

As written above, you can also omit the msg.comand property, in this case the command is taken from the node's settings.