/*
 * @copyright 2020 Stephan Kreyenborg <stephan@kreyenborg.koeln>
 *
 * @author 2020 Stephan Kreyenborg <stephan@kreyenborg.koeln>
 *
 * Dieses Skript dient zur freien Verwendung in ioBroker zur erweiterten Steuerung der iRobot Roboter.
 * Jegliche Verantwortung liegt beim Benutzer. Das Skript wurde unter Berücksichtigung der bestmöglichen Nutzung
 * und Performance entwickelt.
 * Der Entwickler versichert, das keine böswilligen Systemeingriffe im originalen Skript vorhanden sind.
 *
 * Sollte das Skript wider Erwarten nicht korrekt funktionieren, so hast Du jederzeit die Möglichkeit, Dich auf
 * https://www.kreyenborg.koeln
 * für Unterstützung zu melden. Jedes Skript besitzt seine eigene Kommentarseite, auf der,
 * nach zeitlicher Möglichkeit des Autors, Hilfe angeboten wird. Ein Anrecht hierauf besteht nicht!
 *
 * Ansprüche gegenüber Dritten bestehen nicht.
 *
 * Skript Name:     iRobot-Control
 * Skript Version:  1.1
 * Erstell-Datum:   13. Oktober 2021
 *
 */
 
// Datenpunkte neu erstellen
var ueberschreiben = false;
 
// Hauptdatenpunkt unterhalb javascript
var datenpunkt = "iRobot."; // Abschließender Punkt !!! WICHTIG !!!
 
// Instanzen der Geräte
var datenpunkt_instanz = "roomba."; // Abschließender Punkt | !!! BITTE NICHT ÄNDERN !!!
var instanz_geraet = [0, 1]; // Installierte Instanz | Bsp: 0 für Roomba, 1 für Braava
var instanz_name = ["Roomba", "Braava"]; // Keine Leerzeichen | Namen der Geräte ohne Leerzeichen
 
function abonniere_datenpunkte() {
    for (let i = 0; i < instanz_geraet.length; i++) {
        // Abonniere Datenpunkt zu Details des Roboters
        on({ id: datenpunkt_instanz + instanz_geraet[i] + ".device._rawData", change: "any" }, function (obj) {
            // Hole Datenpunkt
            var j = JSON.parse(obj.state.val);
 
            // Aktualisiere alle Datenpunkte
            var tmp_id = obj.id.split(".");
            var id = tmp_id[1];
            aktualisiere_datenpunkte(id, j);
        });
        // Abonniere eigene Datenpunkte zur Steuerung
        on({ id: "javascript.0." + datenpunkt + instanz_name[i] + ".Steuerung", change: "any" }, function (obj) {
            var tmp_id = obj.id.split(".");
            robot_steuerung(instanz_name.indexOf(tmp_id[tmp_id.length - 2]), obj.state.val, "Skript-Control");
        });
        // Abonniere eigene Datenpunkte für geplante Bereiche
        on({ id: "javascript.0." + datenpunkt + instanz_name[i] + ".Geplante_Bereiche", change: "any" }, function (obj) {
            var tmp_id = obj.id.split(".");
            if (obj.state.val != "" || obj.state.val != null) {
                robot_auftrag(instanz_name.indexOf(tmp_id[tmp_id.length - 2]), obj.state.val);
            }
        });
    }
}
 
// Reinigungsauftrag
function robot_auftrag(idRoboter, bereiche) {
    // Basis JSON Objekt
    let j_kommando = {
        command: "start",
        ordered: 1,
        pmap_id: null,
        regions: [],
        user_pmapv_id: null
    };
 
    var array_bereiche = bereiche.split(",");
    // Lese alle vorhandenen Räume ein
    var vorhandene_bereiche = $('state[id=javascript.0.' + datenpunkt + instanz_name[idRoboter] + '.*.*.name]');
    vorhandene_bereiche.each(function (id, i) {
        // Trenne den Datenpunkt
        var tmp_id = id.split(".");
        // lesbarer Name des Bereichs
        var bereich_name = getState(id).val;
        // ID des Bereichs (Raum oder Zone)
        var bereich_id = tmp_id[tmp_id.length - 2];
        // Typ -> Zone oder Raum
        var typ = tmp_id[tmp_id.length - 3];
        // Karte
        var karte = getState(datenpunkt + instanz_name[idRoboter] + ".Bereiche." + tmp_id[tmp_id.length - 4] + ".pmap_id").val;
        // Version der Karte
        var version = getState(datenpunkt + instanz_name[idRoboter] + ".Bereiche." + tmp_id[tmp_id.length - 4] + ".user_pmapv_id").val;
 
        for (let i = 0; i < array_bereiche.length; i++) {
            // Sortiere die Räume und Zonen ein
            if (bereich_name == array_bereiche[i].trim()) {
                j_kommando.pmap_id = karte;
                j_kommando.user_pmapv_id = version;
 
                if (typ == "Raeume") {
                    let tmp = { region_id: bereich_id.toString(), type: "rid" };
                    j_kommando.regions.push(tmp);
                }
                if (typ == "Zonen") {
                    let tmp = { region_id: bereich_id.toString(), type: "zid" };
                    j_kommando.regions.push(tmp);
                }
            }
        }
    });
    let kommando = JSON.stringify(j_kommando);
    setState(datenpunkt_instanz + idRoboter + ".commands._runCommand", kommando);
}
 
// Roboter Steuerung
/**
* @param {string | number} id
* @param {string} kommando
* @param {string} app
*/
function robot_steuerung(id, kommando, app) {
    let j_command = { command: kommando, time: Date.now() / 1000 | 0, initiator: app };
    let command = JSON.stringify(j_command);
 
    // Prüfe, von wem das Kommando kam
    if (app == "Skript-Control") {
        setState("roomba." + id + ".commands._runCommand", command);
    } else {
        setState(datenpunkt + instanz_name[id] + ".Steuerung", kommando, true);
    }
}
 
// Lege neuen Raum an
function neuer_raum(geraet, pmap_id, region_id, region_type, user_pmapv_id) {
    // Bereiche
    createState(datenpunkt + geraet + ".Bereiche." + pmap_id, '', { name: 'ID der Karte', type: 'string', role: 'state' }, function () { });
    createState(datenpunkt + geraet + ".Bereiche." + pmap_id + ".pmap_id", pmap_id, { name: "ID der Karte", type: 'string', role: 'value' }, function () { });
    createState(datenpunkt + geraet + ".Bereiche." + pmap_id + ".user_pmapv_id", user_pmapv_id, { name: "Version der Karte", type: 'string', role: 'value' }, function () { });
 
    // Raum
    if (region_type == "rid") {
        createState(datenpunkt + geraet + ".Bereiche." + pmap_id + '.Raeume.' + region_id, '', { name: 'ID des Raumes', type: 'string', role: 'state' }, function () { });
        createState(datenpunkt + geraet + ".Bereiche." + pmap_id + '.Raeume.' + region_id + '.region_id', region_id, { name: 'ID Des Raumes', type: 'string', role: 'value' }, function () { });
        createState(datenpunkt + geraet + ".Bereiche." + pmap_id + '.Raeume.' + region_id + '.name', '', { name: 'Eigener Name des Raumes', type: 'string', role: 'value' }, function () { });
        createState(datenpunkt + geraet + ".Bereiche." + pmap_id + '.Raeume.' + region_id + '.erstellt', hole_datum(), { name: 'Datum des Hinzufügens', type: 'string', role: 'value' }, function () { });
        log("iRobot-Control: Neuer Raum für " + geraet + " gefunden und angelegt! ID: " + region_id);
    }
 
    // Schmutzzone
    if (region_type == "zid") {
        createState(datenpunkt + geraet + ".Bereiche." + pmap_id + '.Zonen.' + region_id, '', { name: 'ID der Zone', type: 'string', role: 'state' }, function () { });
        createState(datenpunkt + geraet + ".Bereiche." + pmap_id + ".Zonen." + region_id + '.region_id', region_id, { name: 'ID der Zone', type: 'string', role: 'value' }, function () { });
        createState(datenpunkt + geraet + ".Bereiche." + pmap_id + ".Zonen." + region_id + '.name', '', { name: 'Eigener Name der Zone', type: 'string', role: 'value' }, function () { });
        createState(datenpunkt + geraet + ".Bereiche." + pmap_id + '.Zonen.' + region_id + '.erstellt', hole_datum(), { name: 'Datum des Hinzufügens', type: 'string', role: 'value' }, function () { });
        log("iRobot-Control: Neue Zone für " + geraet + " gefunden und angelegt! ID: " + region_id);
    }
}
 
// Aktualisiere alle Datenpunkte
function aktualisiere_datenpunkte(id, j) {
    // Generelle Datenpunkte
    // Gereinigte Quadratmeter
    setState(datenpunkt + instanz_name[id] + ".Quadratmeter", parseFloat((j.bbrun.sqft / 10.764).toFixed(2)), true);
    // Prüfe Wassertank des Braava
    if (j.cleanMissionStatus.notReady === 31) {
        setState(datenpunkt + instanz_name[id] + ".Wassertank", true, true);
    } else {
        setState(datenpunkt + instanz_name[id] + ".Wassertank", false, true);
    }
    // Wischtuch des Braava
    if (j.detectedPad != null) {
        setState(datenpunkt + instanz_name[id] + ".Wischtuch", j.detectedPad, true);
    }
 
    // Aktuelle Räume im Auftrag
    setState(datenpunkt + instanz_name[id] + ".Durchgang_Raeume", generiere_raumliste(id, j.lastCommand.pmap_id, j.lastCommand.regions), true);
 
    // Prüfe, ob kein Training stattfindet
    if (j.lastCommand.regions != null) {
        var region_id = j.lastCommand.regions[0].region_id;
        var region_type = j.lastCommand.regions[0].type;
        var pmap_id = j.lastCommand.pmap_id;
        var user_pmapv_id = j.lastCommand.user_pmapv_id;
 
        // Region Räume
        if (region_type == "rid") {
            // Prüfe, ob Raum existiert
            if (!existsState(datenpunkt + instanz_name[id] + ".Bereiche." + pmap_id + ".Raeume." + region_id + ".region_id")) {
                // Raum existiert nicht. Raum anlegen
                neuer_raum(instanz_name[id], pmap_id, region_id, region_type, user_pmapv_id);
            } else {
                if (user_pmapv_id != null) {
                    setState(datenpunkt + instanz_name[id] + ".Bereiche." + pmap_id + ".user_pmapv_id", user_pmapv_id, true);
                }
            }
        }
        // Region Zonen
        if (region_type == "zid") {
            // Prüfe, ob Raum existiert
            if (!existsState(datenpunkt + instanz_name[id] + ".Bereiche." + pmap_id + ".Zonen." + region_id + ".region_id")) {
                // Raum existiert nicht. Raum anlegen
                neuer_raum(instanz_name[id], pmap_id, region_id, region_type, user_pmapv_id);
            } else {
                if (user_pmapv_id != null) {
                    setState(datenpunkt + instanz_name[id] + ".Bereiche." + pmap_id + ".user_pmapv_id", user_pmapv_id, true);
                }
            }
        }
    }
}
 
// Generiere Raumliste
function generiere_raumliste(id, karte, raeume) {
    if (raeume == null) {
        return "Keine Räume im Auftrag";
    } else {
        // Lege Array an
        var raumliste_name = new Array();
        // Irritiere über die übermittelten Räume
        for (let i = 0; i < raeume.length; i++) {
            var name = "";
            if (raeume[i].type == "rid") {
                if (existsState(datenpunkt + instanz_name[id] + ".Bereiche." + karte + ".Raeume." + raeume[i].region_id + ".name")) {
                    name = getState(datenpunkt + instanz_name[id] + ".Bereiche." + karte + ".Raeume." + raeume[i].region_id + ".name").val;
                    if (name == "" || name == null) {
                        name = "Namenloser Raum mit der ID: " + raeume[i].region_id;
                    }
                } else {
                    // Raum noch nicht vorhanden
                    name = "Nicht definierter Raum (ID: " + raeume[i].region_id + ")";
                }
            }
            if (raeume[i].type == "zid") {
                if (existsState(datenpunkt + instanz_name[id] + ".Bereiche." + karte + ".Zonen." + raeume[i].region_id + ".name")) {
                    name = getState(datenpunkt + instanz_name[id] + ".Bereiche." + karte + ".Zonen." + raeume[i].region_id + ".name").val;
                    if (name == "" || name == null) {
                        name = "Namenlose Zone mit der ID: " + raeume[i].region_id;
                    }
                } else {
                    // Raum noch nicht vorhanden
                    name = "Nicht definierte Zone (ID: " + raeume[i].region_id + ")";
                }
            }
            if (name != "") {
                raumliste_name.push(name);
            }
        }
        // Gebe Raumliste zurück
        if (raumliste_name.length == 0) {
            return "Keine Räume im Autrag!";
        } else {
            return raumliste_name.join(", ");
        }
    }
}
 
// Datum
function hole_datum() {
    let datum = new Date();
    let tag = '0' + datum.getDate();
    let monat = '0' + (datum.getMonth() + 1);
    let jahr = datum.getFullYear();
    let stunde = '0' + datum.getHours();
    let minute = '0' + datum.getMinutes();
    let sekunde = '0' + datum.getSeconds();
    return tag.substr(-2) + '.' + monat.substr(-2) + '.' + jahr + ' ' + stunde.substr(-2) + ':' + minute.substr(-2) + ':' + sekunde.substr(-2);
}
 
// Werte für die Datenpunkte
// iRobot Objekte
var objekt = [
    "Steuerung",
    "Quadratmeter",
    "Wassertank",
    "Wischtuch",
    "Durchgang_Raeume",
    "Geplante_Bereiche"
];
 
// Beschreibung der Objekte
var beschreibung = [
    "Steuerung des Roboters",
    "Gereinigte Quadratmeter",
    "Wassertank des Braava",
    "Montiertes Wischtuch des Braava",
    "Räume des aktuellen Durchgangs",
    "Geplante Bereiche für den Durchgang"
];
 
// Einheiten der Objekte
var einheiten = [
    "",
    "qm²",
    "",
    "",
    "",
    ""
    ,];
 
// Typen der Objekte
var typen = [
    "string",
    "number",
    "boolean",
    "string",
    "string"
];
 
// Rollen der Objekte
var rolle = [
    "action",
    "value",
    "inidcator",
    "value",
    "value",
    "string"
];
 
// Erstelle die benötigten Datenpunkte
function datenpunkte_erstellen() {
    for (let g = 0; g < instanz_geraet.length; g++) {
        for (let i = 0; i < objekt.length; i++) {
            createState(datenpunkt + instanz_name[g] + "." + objekt[i], null, ueberschreiben, {
                name: beschreibung[i],
                desc: beschreibung[i],
                type: typen[i],
                role: rolle[i],
                unit: einheiten[i]
            });
        }
    }
    abonniere_datenpunkte();
}
 
function irobot_erster_start() {
    log("iRobot-Control: Erster Start des Skriptes! Datenpunkte werden erstellt!");
    // Datenpunkte werden erstellt
    if (existsState(datenpunkt_instanz + "0.device._rawData")) {
        datenpunkte_erstellen();
    } else {
        stopScript("");
        log("iRobot-Control: Fehler: Roomba Adapter scheint nicht installiert zu sein!", "error");
    }
}
 
// Erster Start und Initialisierung
irobot_erster_start();