Here's the first cut of Anthem IP control pluging

Hey Kryten,
Got me pretty confused. Sometimes it seems to work for an action or two and sometimes it wont work for any action. When it fails I have to reset the IP configuration to Auto then back to manual.

I just tried START and the status for the input, volume level etc reads OK from the socket.
Then when I clicked info I get:

AVM onChangeRequest-MediaCommand:Info
Z1SIM0012
command: Z1SIM0012
Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host…
Anthem onDisconnect
Anthem onConnect
No connection could be made because the target machine actively refused it.
Anthem onDisconnect

It’s like the receiver just decides to lock the connection - even though I’ve never had an issue with a command from the command prompt using telnet.
I have tried a few variations of port forwarding (thinking there may be some issue between the telnet normal port 23 and the Anthem port of 14999 but none seem to make any difference. Have you had to setup something like that?

Thanks again,

I may have made corrective changes in the past couple of years. Please try this version which is working on both my Anthem receivers reliably.

plugin.Name = "AnthemAVMController";
plugin.OnChangeRequest = onChangeRequest;
plugin.OnConnect = onConnect;
plugin.OnDisconnect = onDisconnect;
plugin.OnPoll = onPoll;
plugin.OnSynchronizeDevices = onSynchronizeDevices;
// Note the polling interval can dynamically change 
plugin.PollingInterval = 250;
plugin.DefaultSettings = { "Host": "10.1.1.97", "Port": "14999" };

var socket = new TCPClient();
var zoneCount = 1; // Change to 2 if you want to use both zones supported

var unprocessedData = "";
var rawVolume;
var data = null;
var _readBuffer = "";
var _eol = ";";

function readMessage() {
    
    for(var wait = 1; wait < 50; wait++) {
        if(socket.available > 0)
            break;
        sleep(1);
    }
    
    if(socket.available <= 0)
        return "";
    
    while (socket.available) {
        var eolIndex = _readBuffer.indexOf(_eol);
        if (eolIndex != -1) {
            var message = _readBuffer.substring(0, eolIndex + 1);
            if (eolIndex + 2 < _readBuffer.length)
                _readBuffer = _readBuffer.substring(eolIndex + 1);
            else
                _readBuffer = "";
            return message;
        }
        else {
            var data = socket.receive();
            if (!data) {
                console.log("AVM: end of stream reached");
                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 {
                throw "MediaCommand value 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":
            var cmdPwr = "Z" + device.Id + "POW" + ((value == "On") ? "1" : "0") + ";";
            if (value == "On") 
                // send power-on twice, the first to wake up device from ECO mode (if enabled)
                cmd = cmdPwr + cmdPwr;
            else
                cmd = cmdPwr;
            break;
        case "Volume":
            var newvol = Math.round(((value - 100) * 90 / 100));
            cmd = "Z" + device.Id + "VOL" + newvol.toString() + ";";
            break;
        case "SoundMode":
            var val1 = String(value).substring(0,1);
            if(val1 < '0' || val1 > '9') {
                throw "AVM: AudioMode out of range (0-9)";
            }
            if (val1 == '0') {
                cmd = "SDVL000;SDVS000;"; // turn off dolby volume leveler
            }
            else {
                cmd = "SDVS001;SDVL00" + val1 + ";"; // turn on dolby volume leveler
            }
            
            break;
        default:
            throw "Not implemented!";
    }
    console.log("AVM: command: " + cmd);
    socket.send(cmd);
}

function onConnect() {
    console.log("AVM: onConnect");
    socket.connect(plugin.Settings["Host"], parseInt(plugin.Settings["Port"]));
    socket.send("SIP1;"); // enable standby IP control
}

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

function onPoll() {
    for (var i = 1; i <= zoneCount; i++) {
        var id = i.toString();
        socket.send("Z" + id + "POW?;");
    }

    var message = null;
    
    while(true) {        
        try {
            message = readMessage();
        }
        catch(err)
        {
            console.log("AVM: Exception during readMessage()");
        }
        
        if (!message || message == "") {
            break;
        }
        
        if(message.substring(0,2) == "!E") {
            console.log("AVM: Processor signalled error: " + message);
            continue; // get another message if available
        }
        
        console.log("Msg: " + message);
        if (message.length <= 5)
            continue;
        
        switch(message.charAt(0)) {
            case 'Z':
                var id = 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 device = plugin.Devices[id];
                switch (messageId) {
                    case "VOL":
                        var vol = parseInt(val3);
                        var vol2 = Math.round(100 + (100 * vol / 90));
                        device.Volume = vol2;
                        console.log("AVM: Volume: " + device.Volume.toString());
                        break;
                    case "POW":
                        device.Switch = (val1 == "1" ? "On" : "Off");
                        if (device.Switch == "On") {
                            // If this device is on, let's query the mute status,
                            // volume and input selection
                            plugin.PollingInterval = 200;
                            socket.send("Z" + id + "MUT?;");
                            socket.send("Z" + id + "VOL?;");
                            socket.send("Z" + id + "INP?;");
                            socket.send("SDVL00?;");
                        }
                        else
                        {
                            plugin.PollingInterval = 1000;
                        }
                        break;
                    case "MUT":
                        device.Mute = (val1 == "1" ? "Muted" : "Unmuted");
                        break;
                    case "INP":
                        device.InputSource = parseInt(val2).toString();
                        break;
                    default:
                        console.log("AVM: unknown Z data read: " + message);
                        break;
                }
                break;
            case 'S':
                var messageId = message.substring(1, 4);
                // "S" commands operate on the primary zone (device)
                var device = plugin.Devices[1];
                switch (messageId) {
                    case "DVL":
                        device.SoundMode = message.substring(6, 7);
                        console.log("Sound mode: " + device.SoundMode.toString());
                        break;
                    case "DVS":
                        break;
                    default:
                        console.log("AVM: unknown S data read: " + message);
                        break;
                }
                break;
        }
    }
}

function onSynchronizeDevices() {
    console.log("AVM: onSynchronizeDevices");
    //Generate zones
    //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", "AudioVolume", "MediaControl", "MediaInputSource", "Switch", "AudioSoundMode"];
        device.SupportedSoundModes = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ];
        device.DeviceType = "AudioZone";
        device.TileTemplate = "AudioZoneTile.xaml";
        device.DetailsTemplate = "AudioZoneDetails.xaml";

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

Kryten,
So I found my issue! I have been working on a quickapp for the Fibaro HC3 which I have at home. During the development of that I experienced the same issues. After some discussion with Anthem and others - I worked out that the receiver has two MAC addresses and IP addresses - one for the receiver and the other for Play-fi. After I just ensured the reserved IP for the receiver had the right MAC address and everything seems really stable now.

So now I have a new issue. I prefer to have my home automation setup have the quickapp rather than the home remote but for some reason the quickapp is not imported when I synchronize my devices. I’m not sure how to setup the quickapp to be compatible with the Fibaro device declaration. I assume that I need to make the device type and capabilities the same as the home remote options. I’ll keep working on it but if you have any further ideas I’d appreciate it.
Thanks
Anthony

So you’re trying to pull the Anthem into THR through Fibaro and not use this plugin? Sorry its a little confusing.

If there is a direct (local control not cloud based) method to control your Anthem like this plug-in, that’s the way to go! Fibaro and THR can both communicate to the Anthem at the same time. I do this with HomeAssistant and THR… HA for automations and THR for user inputs (like volume control).

Hey Jdamore,
So I try to have all my devices in my house centralised in the Fibaro HC3. It’s not cloud based - just local network.
I wasn’t aware though that you can have two sessions at the same time - using the same IP and Port? I’ll give it a try.
Thanks.
Anth

Correct… same IP/port. I’m not sure if there is a limit to the number of connections but, I have 3 tablets (dedicated to THR), HomeAssistant, and occasionally my phone (THR) all simultaneously connected to my two MRX740’s.

Jdamore - yep - working really well! Just transferring all of the functions to my screens.

1 Like