Here's the first cut of Anthem IP control pluging

I’m using this to control my AVM60, but the same interface is used for other products in Anthem’s range like the MRX series. As such, a few things might be hardcoded specific to the AVM60 (like 2 zones), but that could easily be modified as required.

plugin.Name = "AnthemAVMController";
plugin.OnChangeRequest = onChangeRequest;
plugin.OnConnect = onConnect;
plugin.OnDisconnect = onDisconnect;
plugin.OnPoll = onPoll;
plugin.OnSynchronizeDevices = onSynchronizeDevices;
plugin.PollingInterval = 1000;
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 = ";";

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 {
                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 "Volume":
            var newvol = Math.round(((value - 100) * 90 / 100));
            cmd = "Z" + device.Id + "VOL" + newvol.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 + "INP?;");
    }
}

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

function onPoll() {
    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 device = plugin.Devices[deviceId];
        switch (messageId) {
            case "VOL":
                var vol = parseInt(val3);
                var vol2 = 100 + (100 * vol / 90);
                device.Volume = vol2;
                console.log("Volume: " + device.Volume);
                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;
        }
    }
}

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", "AudioVolume", "MediaControl", "MediaInputSource", "Switch"];
        device.DeviceType = "AudioZone";
        device.TileTemplate = "AudioZoneTile.xaml";
        device.DetailsTemplate = "AudioZoneDetails.xaml";

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