Here's the first cut of Anthem IP control pluging

You are awesome! I’ll load this up tomorrow.

Thanks for this. Here’s my tweaked version. I’m very new to Home Remote so there may -not- be some ‘best practice’ stuff here…!

The changes are:-

  • Volume in Decibels
  • Addition of a MuteToggle Command
  • Generation of status text which can be bound to a label. This gives audio format, bitrate and video mode. This is returned in the ‘SoundMode’
plugin.Name = "AnthemAVMController";
plugin.OnChangeRequest = onChangeRequest;
plugin.OnConnect = onConnect;
plugin.OnDisconnect = onDisconnect;
plugin.OnPoll = onPoll;
plugin.OnSynchronizeDevices = onSynchronizeDevices;
plugin.PollingInterval = 0;
plugin.DefaultSettings = { "Host": "10.1.1.1", "Port": "14999" };

var socket = new TCPClient();
var zoneCount = 2;

var unprocessedData = "";
var rawVolume;
var data = null;
var options = { timeout: 2000 };
var _readBuffer = "";
var _eol = ";";

var AIN= "";
var AIR = "";
var VIR = "";
var AIC = "";

function readMessage() {
    while (true) {
        var eolIndex = _readBuffer.indexOf(_eol);
        if (eolIndex != -1) {
            var message = _readBuffer.substring(0, eolIndex + 1);
            _readBuffer = _readBuffer.substring(eolIndex + 1);
            return message;
        }
        else {
            var data = socket.receive();
            if (!data) {
                throw "End of the stream";
            }
            _readBuffer = _readBuffer + data;
        }
    }
}

function onChangeRequest(device, attribute, value) {
    console.log("AVM onChangeRequest-" + attribute + ":" + value);
    var cmd = null;
    switch (attribute) {
        case "MediaCommand":
            if (value == "VolumeUp") {
                cmd = "Z" + device.Id + "VUP01"
            }
            else if (value == "VolumeDown") {
                cmd = "Z" + device.Id + "VDN01"
            }
            else if (value == "MuteToggle") {
                cmd = "Z" + device.Id + "SIM0027"
            }
            else {
                throw "not implemented";
            }
            break;
        case "InputSource":
            cmd = "Z" + device.Id + "INP" + parseInt(value).toString().padStart(2, '0');
            break;
        case "Mute":
            cmd = "Z" + device.Id + "MUT" + ((value == "Muted") ? "1" : "0");
            break;
        case "Switch":
            cmd = "Z" + device.Id + "POW" + ((value == "On") ? "1" : "0");
            break;
        case "VolumeDecibel":
            cmd = "Z" + device.Id + "VOL" + value.toString();
            break;
        default:
            throw "Not implemented!";
    }
    console.log("command: " + cmd);
    cmd = cmd + ";";
    socket.send(cmd);
}

function onConnect() {
    console.log("AVM onConnect");
    socket.connect(plugin.Settings["Host"], parseInt(plugin.Settings["Port"]));
    for (var i = 1; i <= zoneCount; i++) {
        var id = i.toString();
        socket.send("Z" + id + "POW?;");
        socket.send("Z" + id + "MUT?;");
        socket.send("Z" + id + "VOL?;");
        socket.send("Z" + id + "AIR?;");
        socket.send("Z" + id + "AIN?;");
        socket.send("Z" + id + "AIC?;");
        socket.send("Z" + id + "VIR?;");
        socket.send("Z" + id + "INP?;");
    }
}

function onDisconnect() {
    console.log("AVM onDisconnect");
    socket.close();
}

function onPoll() {
    var dirtystatus = 0;
    var message = readMessage();
    console.log("Msg: " + message);
    if (message.length > 5 && message.charAt(0) == 'Z') {
        var deviceId = message.substring(1,2).toString();
        var messageId = message.substring(2, 5);
        var val1 = message.substring(5,6);
        var val2 = message.substring(5,7);
        var val3 = message.substring(5,8);
        var valfull = message.substring(5,message.length-1)
        var device = plugin.Devices[deviceId];
        switch (messageId) {
            case "VOL":
                var vol = parseInt(val3);
                device.VolumeDecibel = vol;
                console.log("Volume: " + device.Volume);
                break;
            case "AIR":
                AIR = valfull;
                console.log ("AIR: " + AIR);
                dirtystatus=1;
                break;
            case "AIN":
                AIN = valfull;
                console.log ("AIN: " + AIN);
                dirtystatus=1;
                break;
            case "AIC":
                switch (val1) {
                    case "1":
                       AIC = "Other";
                       break;
                    case "2":
                       AIC = "1.0";
                       break;
                    case "3":
                       AIC = "2.0";
                       break;
                    case "4":
                       AIC = "5.1";
                       break;
                    case "5":
                       AIC = "6.1";
                       break;
                    case "6":
                       AIC = "7.1";
                       break;
                    default:
                       AIC="";
                       break
                 }
                console.log ("AIC: " + AIC);
                dirtystatus=1;
                break;    
            case "VIR":
                switch (val1) {
                    case "2":
                       VIR = "1080p60";
                       break;
                    case "3":
                       VIR = "1080p50";
                       break;
                    case "4":
                       VIR = "1080p24";
                       break;
                    case "5":
                       VIR = "1080i60";
                       break;
                    case "6":
                       VIR = "1080i50";
                       break;
                    case "7":
                       VIR = "720p60";
                       break;
                    case "8":
                       VIR = "720p50";
                       break;
                    case "9":
                       VIR = "576p50";
                       break;
                    case "10":
                       VIR = "576i50";
                       break;
                    case "11":
                       VIR = "480p60";
                       break;
                    case "12":
                       VIR = "480i60";
                       break;
                    case "13":
                       VIR = "3D";
                       break;
                    case "14":
                       VIR = "4K";
                       break;
                    default:
                       VIR="Unknown";
                       break
                 }
                console.log ("VIR: " + VIR);
                dirtystatus=1;
                break;
            case "POW":
                device.Switch = (val1 == "1" ? "On" : "Off");
                break;
            case "MUT":
                device.Mute = (val1 == "1" ? "Muted" : "Unmuted");
                break;
            case "INP":
                device.InputSource = parseInt(val2);
                break;
            default:
                console.log("unknown data read: " + message);
                break;
        }
    }
    
    if (dirtystatus == 1) {
    	if (VIR == "Unknown") 
    	{
    		device.SoundMode = "No Input";
    	} else
    	{
    	    device.SoundMode = AIN + " " + AIR + " " + AIC + " (" + VIR + ")"
      }
    }
}

function onSynchronizeDevices() {
    console.log("AVM onSynchronizeDevices");
    //Generate zones 1 and 2
    //First digit in Id is the zone number.
    for (var i = 1; i <= zoneCount; i++) {
        var device = new Device();
        device.Id = i.toString();
        device.DisplayName = "AVM Zone " + i.toString();
        device.Icon = "Receiver";
        device.Capabilities = ["AudioMute", "AudioSoundMode", "AudioVolumeDecibel", "MediaControl", "MediaInputSource", "Switch"];
        device.DeviceType = "AudioZone";
        device.TileTemplate = "AudioZoneTile_hw.xaml";
        device.DetailsTemplate = "AudioZoneDetails_hw.xaml";

        plugin.Devices[device.Id] = device;
    }
}
2 Likes

Thanks for this plugin, although I don’t (yet?) have an Anthem. I was going to pick up a MRX 1140 8k to replace my Denon AVR-X4500H, but many are telling me to stay away from “boutique” brands.

Are you folks happy with the Anthems and what are the biggest selling points for you?

I love mine. ARC is brilliant - I still think it’s the best room correction system without going mad on stuff like Lyngdorf

User interface is basic but flexible.

The amplification is good.

1 Like

I don’t consider Anthem to be “boutique” at all. Sure, they’re low-volume when compared to a Sony or Denon, but they have excellent QA, great design, and good support. I’m on my third product of theirs and have loved their stuff.

1 Like

I have two MRX-740s in my rack and LOVE them. This plug in works great and I haven’t had any control issues in the last year. Like I mentioned above, adding a web browser to your Home Remote gives you all the control.

My previous Marantz and Denon receivers had more bells and whistles but, doesn’t compare in sound quality after running the room correction (and some manual tweaks). My biggest reason for upgrading was sound and to get the latest audio formats. For me, 8k is beyond a resolution my eye balls can distinguish so personally I’ll never pay you update my units. If for whatever reason I outgrow the MRX, I’ll stick with Anthem and move to the AVM.

My only complaint is the spotify connect feature is absolutely dogsh!t. Countless times I’ve had volume sync issues between my phone (spotify app) and the MRX and the volume blasts out at damaging levels. All i need is the ability to disable volume from the spotify app. Imagine how terrible my friends feel when they connect (group session) and the MRX syncs to their phones volume level (full lol) I had multiple conversations with their support about this and they actually got their engineering involved once I showed them how to reproduce the issue. After 6 months there was still no fix so I just moved onto dedicated streamer for Spotify. I don’t hold this against them in anyway… they really put a lot of effort into my issue.

1 Like

Thank you @stevelup @Kryten67 & @Jdamore for the feedback and not giving me heck for hijacking the thread. :slight_smile: Reviews and specs look decent, but I’ll have to try and get a listen. Plus I would love to support a local (Canadian) company, too.

I was looking at the MRX-1140 but given that I’ve already got L/C/R Outlaw 2200 monoblocks and a MiniDSP HD 2x45 to tune the 3 subs, the MRX-740 might be fine after all (I probably only have a small room and space for one more pair of surrounds/heights anyhow). I would be opting for the 8k board, as my PJ can support 4k@120. And I’m not worried about streaming as I have an offboard Bluesound Node.

Now…back to our regularly scheduled programming, I guess (pun intended).

Thank you again for this plugin. I pulled the trigger on the MRX 1140 yesterday and with this plug-in, I’m up and running with the new AVR in less than an hour.

1 Like

Command question for you folks (I have also reached out to Anthem).

From what I can tell from the v5 command reference for the MRX-x40-AVM-70-90, it’s possible to get/set the name of a speaker profile (using SSPp0), and set the speaker profile for a given input (using ISiSPp). However, I can’t seem to find out how to change the speaker profile on the fly. Is this possible?

I know I could do probably do this through creating additional discrete inputs, each with the different profiles assigned, but programmatically, it would be easier to set the input and profiles independenty depending on the use case (e.g. listening positions).

In the end, I want to add icons that will allow the user to say if they are sitting in the front row, back row, or both, and show the status of the one that’s selected.

If you can telnet to the Anthem port, then you can try out various commands to see live what is possible. Get familiar with the interface first by issuing commands like “VOL+” to see how Anthem responds in the terminal session.

I’m very familiar with it, and I have the command reference. I just don’t see a command that can do what I noted above.

OK, then you’ll definitely need to speak to Anthem support.

I just loaded this up. Thanks! I wanted to know my soundmode and volume in decibels.

Couple of things to note… Your volume attribute is “VolumeDecibel”. I changed it to “Volume” otherwise I would have to remap all my elements.
Because decibels is a different number range (-90db to whatever your “Maximum Volume” is on the Anthem), anyone changing to decibels with need to update the MIN/MAX property for their volume slider.
Decibels feedback still shows a “%”… no biggie… just limit the size of the label to cut off the %

Thanks again! Love the sound mode displayed:

Thats the problem… the command can only be sent for the input you want to configure. I doubt Anthem will comeback with a command to do what you want. I think what you’re doing is a good idea BTW, I take it your Speaker Profiles pin-point the location of the listener with different distance/level settings?

I think your either going to have to create duplicate inputs like you mention, or, you will need to fenagle THR buttons to send the proper ISiPSp command based off of .InputSource. There’s probably a couple different ways you could it.

1 Like

Ye that’s what I intended; one setting for front row and one for both rows (or just rear).

For some reason I saw configuring the input with a different speaker profile to be undesirable. But in reality it’s not a big deal I suppose, and while I have already created the duplicate inputs and logic to implement, it should be easy to do through ISiPSp commands.

I’ll give it a whirl tomorrow, when I also plan to update the script to add nav support.

1 Like

So…this was so much easier than I thought :slight_smile:

In the older x20 series, you could set the profile to the active input. This isn’t available in the x40 for some reason. Also, there is a documentation bug, where the profiles actually start at ‘0’ for the first profile (not ‘1’) as documented, etc…

In my case, the only inputs that need to have different seating positions are the Apple TV (i=7) and Blu-Ray (i=6). My Front Row profile is in the first slot (p=0) and the Back Row profile is in the second slot (p=1).

Changes to the script:

added this block above case “MediaCommand”:

    case "SeatingPosition":
        if (value == "Front") { //format ISiSPp ... change i and p as needed
            cmd = "IS6SP0;IS7SP0"; //yes, you can string commands together!
            break;
        }
        else if (value == "Back") {
            cmd = "IS6SP1;IS7SP1";
            break;
        }

also added this in OnSynchronizeDevices after the device.Capabilities line

    device.Attributes = ["SeatingPosition"];

with these, I can now assign the action and trigger the change with a single click.

I also added some basic navigation commands to the MediaCommand case statement (would have been more efficient to use a nested select statement, but I was lazy :slight_smile:

        else if (value == "Setup") { 
            cmd = "Z" + device.Id + "SIM0012"
        }
        else if (value == "Info") { 
            cmd = "Z" + device.Id + "SIM0017"
        }
        else if (value == "DirectionUp") {
            cmd = "Z" + device.Id + "SIM0018"
        }
        else if (value == "DirectionDown") { 
            cmd = "Z" + device.Id + "SIM0019"
        }
        else if (value == "DirectionLeft") { 
            cmd = "Z" + device.Id + "SIM0020"
        }
        else if (value == "DirectionRight") { 
            cmd = "Z" + device.Id + "SIM0021"
        }
        else if (value == "Select") { 
            cmd = "Z" + device.Id + "SIM0022"
        }

Some more tweaks for the seating position.

Added the following to the onPoll function to read status:

if (message.substring(3,6) == 'SP0' ) {
    var deviceId = 1; //1 or 2 ... doesn't matter really since speaker profiles are global
    var device = plugin.Devices[deviceId]; //yes, I could simplify :) 
    console.log ("Front");
    device.SeatingPosition = "Front";
}
if (message.substring(3,6) == 'SP1' ) {
    var deviceId = 1;
    var device = plugin.Devices[deviceId];
    console.log ("Back");
    device.SeatingPosition = "Back";
}

With this, I now have a way to toggle between the two and set visual status accordingly:

Back
Front

(thanks, @Jdamore, for the inspiration to get this implemented…I would have taken a longer, more convoluted route to get this done without your comment!)

I’m attaching my customized version of the script if anyone wants to follow along.
anthem_plugin_script (LJR mods).txt (9.4 KB)

Nice work @LJR
Question… How did you get the “dB” after the value? is that just another label?
I used your plugin script and tossed together a quick example. I put in as many commands as I can think of except for your speaker profile stuff. I’m not sure how to handle that yet…

See Attached Example:
Anthem_Example_2022_12_23.hrp (1.2 MB)

Once you open the example, remember to change the IP (and port if different) of “Anthem_MRX_AVM” (right click > open). Also change the IP in the properties of the WebBrowser.

Sorry @Kryten67 'your original volume attribute has changed from " Volume" to VolumeDecibel". Is it possible to do both in the script? I’m not smart enough for that stuff ha!

Hey @Jdamore … I loaded it up - had to make some small changes to the plugin naming to get it to work.

What I recommend you do is copy the plugin text, delete both the device and plugin script, then then add the plugin via “Import from Code”. That will recreate the device and script, allow you to configure the IP, and it should work immediately without any further changes (I’ve tested this procedure and it works for me)…

Also, be sure to have decible set on the AVR. Otherwise, the integer reported will be incorrect.

Anthem_Example_for jdamore.hrp (1.1 MB)

Now its showing “dB” as it should. Yeah… it was set to % on the MRX (I must have switched it at some point in my tinkering :thinking:)

I updated my example project in my last post.

No need to delete anything. You just need to update your IP address…
Just right-click on the device and open:
image

Change the IP:
image

Also change the IP of the WebBrowser to point it at your Anthem: