Weiter zum Inhalt
  • Home
  • Aktuell
  • Tags
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • GitHub
  • Docu
  • Hilfe
Skins
  • Hell
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dunkel
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Standard: (Kein Skin)
  • Kein Skin
Einklappen
ioBroker Logo

Community Forum

donate donate
  1. ioBroker Community Home
  2. Deutsch
  3. Skripten / Logik
  4. verschiedene Skripte

NEWS

  • Monatsrückblick Juli / August 2026 ist online!
    BluefoxB
    Bluefox
    7
    1
    107

  • Der neue Monatsrückblick für Mai und Juni 2026 ist online!
    BluefoxB
    Bluefox
    8
    1
    1.3k

  • wichtiges UPDATE für controller 7.2.2 im stable
    HomoranH
    Homoran
    10
    1
    3.8k

verschiedene Skripte

Geplant Angeheftet Gesperrt Verschoben Skripten / Logik
javascript
5 Beiträge 2 Kommentatoren 339 Aufrufe 4 Beobachtet
  • Älteste zuerst
  • Neuste zuerst
  • Meiste Stimmen
Antworten
  • In einem neuen Thema antworten
Anmelden zum Antworten
Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.
  • H Online
    H Online
    helfi9999
    schrieb am zuletzt editiert von helfi9999
    #1

    Hallo zusammen,
    habe mal bei mir aufgeräumt und habe eine ganze Menge an Skripten die ich von hier habe und stellenweise auch umgebaut habe die ich jetzt gerne mit euch teilen möchte:

    z.B.
    Musikstatus je Raum mit Alexa und/oder Sonos mit States Erstellung.Voraussetzung ist der Alexa Adapter und der Sonos Adapter:
    884f31a3-f1c5-4988-9e9c-7e4f0f68c5d3-image.jpeg
    Ihr müsst nur die Geräte im Skript ändern.

    
    /*************************************************
    * Musikstatus je Raum
    * mit State-Erstellung + Provider-Erkennung
    *************************************************/
    
    const rooms = [
       {
           name: "Wohnzimmer",
           target: "0_userdata.0.Status_Musikboxen.Wohnzimmer_Status",
           alexaState: "Deine Alexa.Player.currentState",
           alexaProvider: "Deine Alexa.Player.providerName",
           sonosState: "sonos.0.root.192_168_178_XXX.state_simple",
           sonosType: "sonos.0.root.192_168_178_XXX.current_type",
           delayMs: 0,
           config: { alexaDirectProvider: false, sonosMap: {0:"Musik von Platte",1:"TuneIn-Liveradio",2:"Fernseher"} }
       },
       {
           name: "Esszimmer",
           target: "0_userdata.0.Status_Musikboxen.Esszimmer_Status",
           alexaState: "Deine Alexa.Player.currentState",
           alexaProvider: "Deine Alexa.Player.providerName",
           sonosState: "sonos.0.root.192_168_178_XXX.state_simple",
           sonosType: "sonos.0.root.192_168_178_XXX.current_type",
           delayMs: 0,
           config: { alexaDirectProvider: false, sonosMap: {0:"Musik von Platte",1:"TuneIn-Liveradio"} }
       },
       {
           name: "Küche",
           target: "0_userdata.0.Status_Musikboxen.Küche_Status",
           alexaState: "Deine Alexa.Player.currentState",
           alexaProvider: "Deine Alexa.Player.providerName",
           sonosState: "sonos.0.root.192_168_178_XXX.state_simple",
           sonosType: "sonos.0.root.192_168_178_XXX.current_type",
           delayMs: 0,
           config: { alexaDirectProvider: false, sonosMap: {0:"Musik von Platte",1:"TuneIn-Liveradio"} }
       },
       {
           name: "Garten",
           target: "0_userdata.0.Status_Musikboxen.Garten_Status",
           alexaState: "Deine Alexa.Player.currentState",
           alexaProvider: "Deine Alexa.Player.providerName",
           delayMs: 2000,
           config: { alexaDirectProvider: true }
       },
       {
           name: "Schlafzimmer",
           target: "0_userdata.0.Status_Musikboxen.Schlafzimmer_Status",
           alexaState: "Deine Alexa.Player.currentState",
           alexaProvider: "Deine Alexa.Player.providerName",
           delayMs: 2000,
           config: { alexaDirectProvider: true }
       },
       {
           name: "Spielzimmer",
           target: "0_userdata.0.Status_Musikboxen.Spielzimmer_Status",
           alexaState: "Deine Alexa.Player.currentState",
           alexaProvider: "Deine Alexa.Player.providerName",
           delayMs: 0,
           config: { alexaDirectProvider: true }
       },
       {
           name: "Kinderzimmer",
           target: "0_userdata.0.Status_Musikboxen.Kinderzimmer_Status",
           alexaState: "Deine Alexa.Player.currentState",
           alexaProvider: "Deine Alexa.Player.providerName",
           delayMs: 2000,
           config: { alexaDirectProvider: true }
       },
       {
           name: "Gäste WC",
           target: "0_userdata.0.Status_Musikboxen.Gaeste_WC",
           alexaState: "Deine Alexa.Player.currentState",
           alexaProvider: "Deine Alexa.Player.providerName",
           delayMs: 0,
           config: { alexaDirectProvider: true }
       },
       {
           name: "Badezimmer",
           target: "0_userdata.0.Status_Musikboxen.Badezimmer_Status",
           alexaState: "Deine Alexa.Player.currentState",
           alexaProvider: "Deine Alexa.Player.providerName",
           sonosState: "sonos.0.root.192_168_178_XXX.state_simple",
           sonosType: "sonos.0.root.192_168_178_XXX.current_type",
           delayMs: 0,
           config: { alexaDirectProvider: false, sonosMap: {0:"Musik von Platte",1:"TuneIn-Liveradio"} }
       },
       {
           name: "Hobbyraum",
           target: "0_userdata.0.Status_Musikboxen.Hobbyraum_Status",
           alexaState: "Deine Alexa.Player.currentState",
           alexaProvider: "Deine Alexa.Player.providerName",
           sonosState: "sonos.0.root.192_168_178_XXX.state_simple",
           sonosType: "sonos.0.root.192_168_178_XXX.current_type",
           delayMs: 0,
           config: { alexaDirectProvider: false, sonosMap: {0:"Musik von Platte",1:"TuneIn-Liveradio"} }
       }
    ];
    
    /*********************** STATES ERSTELLEN ***********************/
    
    function createTargetState(room) {
       createState(room.target, "nichts", {
           name: `Musikstatus ${room.name}`,
           type: "string",
           role: "text",
           read: true,
           write: true
       });
    }
    
    /*********************** LOGIK ***********************/
    
    function readVal(id, def = null) {
       if (!id) return def;
       const s = getState(id);
       return s ? s.val : def;
    }
    
    function normalizeProvider(p) {
       if (!p) return "";
    
       const raw = String(p).trim();
       const t = raw.toLowerCase();
    
       if (t.includes("amazon")) return "Amazon Music";
       if (t.includes("spotify")) return "Spotify";
       if (t.includes("tunein") || t.includes("radio")) return "TuneIn-Liveradio";
       if (t.includes("audible")) return "Audible";
       if (t.includes("apple")) return "Apple Music";
       if (t.includes("deezer")) return "Deezer";
       if (t.includes("youtube")) return "YouTube";
       if (t.includes("podcast")) return "Podcast";
       if (t.includes("bluetooth") || t.includes("aux") || t.includes("line")) return "Externe Quelle";
    
       return raw;
    }
    
    function writeStatus(room, val) {
       if (room.delayMs > 0) {
           setStateDelayed(room.target, val, true, room.delayMs, true);
       } else {
           setState(room.target, val, true);
       }
    }
    
    function evalRoom(room) {
       const alexaPlaying = readVal(room.alexaState, false) === true;
       const alexaProvider = normalizeProvider(readVal(room.alexaProvider, ""));
       const sonosPlaying = readVal(room.sonosState, false) === true;
       const sonosType = readVal(room.sonosType, null);
    
       if (room.config.alexaDirectProvider) {
           if (alexaPlaying) {
               writeStatus(room, alexaProvider || "Andere Quelle");
               log(`+++ ${room.name}: ${alexaProvider || "Andere Quelle"} +++`, "debug");
           } else {
               writeStatus(room, "nichts");
           }
           return;
       }
    
       if (alexaPlaying && alexaProvider) {
           writeStatus(room, alexaProvider);
           return;
       }
    
       if (
           sonosPlaying &&
           room.config.sonosMap &&
           room.config.sonosMap[sonosType] !== undefined
       ) {
           writeStatus(room, room.config.sonosMap[sonosType]);
           return;
       }
    
       writeStatus(room, "nichts");
    }
    
    function initRoom(room) {
       createTargetState(room);
    
       const ids = [];
    
       if (room.alexaState) ids.push(room.alexaState);
       if (room.alexaProvider) ids.push(room.alexaProvider);
       if (room.sonosState) ids.push(room.sonosState);
       if (room.sonosType) ids.push(room.sonosType);
    
       if (ids.length === 0) {
           log(`Keine Trigger für ${room.name} konfiguriert`, "warn");
           return;
       }
    
       on({ id: ids, change: "ne" }, () => evalRoom(room));
    
       setTimeout(() => {
           evalRoom(room);
       }, 1000);
    }
    
    /*********************** START ***********************/
    
    rooms.forEach(initRoom);
    


    Lautstärken Reset in Verbindung mit dem Skript Musikstatus
    Funktionen:

      • Reagiert auf Statusänderung eines Raums
      • Prüft auf Zielstatus "nichts"
      • Setzt Lautstärke definierter Geräte nach optionaler Verzögerung
      • Verhindert Mehrfach-Timer pro Raum
      • Optionales Debug-Logging
      • Alexa notificationVolume wird explizit über notificationId gesetzt
      • Sonderfall Kinderzimmer: nur notificationVolume, kein Player.volume

    /**
     * Echo Stati / Lautstärke-Reset
     *
     * Funktionen:
     * - Reagiert auf Statusänderung eines Raums
     * - Prüft auf Zielstatus "nichts"
     * - Setzt Lautstärke definierter Geräte nach optionaler Verzögerung
     * - Verhindert Mehrfach-Timer pro Raum
     * - Optionales Debug-Logging
     * - Alexa notificationVolume wird explizit über notificationId gesetzt
     * - Sonderfall Kinderzimmer: nur notificationVolume, kein Player.volume
     */
    
    const CONFIG = {
        targetStatus: 'nichts',
        defaultDelayMs: 5000,
        debug: true,
        logPrefix: '[Musikboxen]',
        minNotificationVolume: 0,
        maxNotificationVolume: 100,
        defaultNotificationOffset: 5
    };
    
    const rooms = [
        {
            name: 'Küche',
            enabled: true,
            statusId: '0_userdata.0.Status_Musikboxen.Küche_Status',
            delayMs: 0,
            trigger: 'ne',
            devices: [
                {
                    id: 'alexa2.0.Echo-Devices.661b5e9aa0b24161a4ec52b340173846.Player.volume',
                    notificationId: 'alexa2.0.Echo-Devices.661b5e9aa0b24161a4ec52b340173846.Preferences.notificationVolume',
                    volume: 10,
                    notificationOffset: 5
                },
                {
                    id: 'alexa2.0.Echo-Devices.4879f7942cdb42d596f4e24aac81e908.Player.volume',
                    notificationId: 'alexa2.0.Echo-Devices.661b5e9aa0b24161a4ec52b340173846.Preferences.notificationVolume',
                    volume: 10,
                    notificationOffset: 5
                },
                {
                    id: 'sonos.0.root.192_168_178_18.volume',
                    volume: 10
                }
            ]
        },
        {
            name: 'Wohnzimmer',
            enabled: true,
            statusId: '0_userdata.0.Status_Musikboxen.Wohnzimmer_Status',
            delayMs: CONFIG.defaultDelayMs,
            trigger: 'any',
            devices: [
                {
                    id: 'alexa2.0.Echo-Devices.025b455db7bb4720ad941b7139bcc805.Player.volume',
                    notificationId: 'alexa2.0.Echo-Devices.d5794400dafd4c2cb0c8889fc15f0653.Preferences.notificationVolume',
                    volume: 10,
                    notificationOffset: 5
                },
                {
                    id: 'alexa2.0.Echo-Devices.d5794400dafd4c2cb0c8889fc15f0653.Player.volume',
                    notificationId: 'alexa2.0.Echo-Devices.d5794400dafd4c2cb0c8889fc15f0653.Preferences.notificationVolume',
                    volume: 10,
                    notificationOffset: 5
                },
                {
                    id: 'sonos.0.root.192_168_178_21.volume',
                    volume: 10
                }
            ]
        },
        {
            name: 'Esszimmer',
            enabled: true,
            statusId: '0_userdata.0.Status_Musikboxen.Esszimmer_Status',
            delayMs: CONFIG.defaultDelayMs,
            trigger: 'any',
            devices: [
                {
                    id: 'alexa2.0.Echo-Devices.084e4de76a484be18aeda245e8f3f4d4.Player.volume',
                    notificationId: 'alexa2.0.Echo-Devices.c31ec0fc26584e078ca0555aecfc9188.Preferences.notificationVolume',
                    volume: 10,
                    notificationOffset: 5
                },
                {
                    id: 'alexa2.0.Echo-Devices.c31ec0fc26584e078ca0555aecfc9188.Player.volume',
                    notificationId: 'alexa2.0.Echo-Devices.c31ec0fc26584e078ca0555aecfc9188.Preferences.notificationVolume',
                    volume: 10,
                    notificationOffset: 5
                },
                {
                    id: 'sonos.0.root.192_168_178_20.volume',
                    volume: 10
                }
            ]
        },
        {
            name: 'Schlafzimmer',
            enabled: true,
            statusId: '0_userdata.0.Status_Musikboxen.Schlafzimmer_Status',
            delayMs: CONFIG.defaultDelayMs,
            trigger: 'ne',
            devices: [
                {
                    id: 'alexa2.0.Echo-Devices.G091MK0614730BNS.Player.volume',
                    notificationId: 'alexa2.0.Echo-Devices.G091MK0614730BNS.Preferences.notificationVolume',
                    volume: 18,
                    notificationOffset: 12
                }
            ]
        },
        {
            name: 'Hobbyraum',
            enabled: true,
            statusId: '0_userdata.0.Status_Musikboxen.Hobbyraum_Status',
            delayMs: CONFIG.defaultDelayMs,
            trigger: 'ne',
            devices: [
                {
                    id: 'alexa2.0.Echo-Devices.d3127dcdc8bc4158807fcab2fcfe7fba.Player.volume',
                    notificationId: 'alexa2.0.Echo-Devices.c9d487971e914e939baad79585dd7b2e.Preferences.notificationVolume',
                    volume: 10,
                    notificationOffset: 5
                }
            ]
        },
        {
            name: 'Gäste WC',
            enabled: true,
            statusId: '0_userdata.0.Status_Musikboxen.Gaeste_WC',
            delayMs: CONFIG.defaultDelayMs,
            trigger: 'ne',
            devices: [
                {
                    id: 'alexa2.0.Echo-Devices.G090LF10718513KQ.Player.volume',
                    notificationId: 'alexa2.0.Echo-Devices.G090LF10718513KQ.Preferences.notificationVolume',
                    volume: 15,
                    notificationOffset: 8
                }
            ]
        },
        {
            name: 'Badezimmer',
            enabled: true,
            statusId: '0_userdata.0.Status_Musikboxen.Badezimmer_Status',
            delayMs: CONFIG.defaultDelayMs,
            trigger: 'any',
            devices: [
                {
                    id: 'alexa2.0.Echo-Devices.828b54738e2a44a29d9331868a046c38.Player.volume',
                    notificationId: 'alexa2.0.Echo-Devices.eba89b7180384792a24f92847baea082.Preferences.notificationVolume',
                    volume: 10,
                    notificationOffset: 5
                },
                {
                    id: 'alexa2.0.Echo-Devices.eba89b7180384792a24f92847baea082.Player.volume',
                    notificationId: 'alexa2.0.Echo-Devices.eba89b7180384792a24f92847baea082.Preferences.notificationVolume',
                    volume: 10,
                    notificationOffset: 5
                },
                {
                    id: 'sonos.0.root.192_168_178_17.volume',
                    volume: 10
                }
            ]
        },
        {
            name: 'Spielzimmer',
            enabled: true,
            statusId: '0_userdata.0.Status_Musikboxen.Spielzimmer_Status',
            delayMs: CONFIG.defaultDelayMs,
            trigger: 'any',
            devices: [
                {
                    id: 'alexa2.0.Echo-Devices.G2A0P3077407031K.Player.volume',
                    notificationId: 'alexa2.0.Echo-Devices.G2A0P3077407031K.Preferences.notificationVolume',
                    volume: 25,
                    notificationOffset: 15
                }
            ]
        },
        {
            name: 'Kinderzimmer',
            enabled: true,
            statusId: '0_userdata.0.Status_Musikboxen.Kinderzimmer_Status',
            delayMs: CONFIG.defaultDelayMs,
            trigger: 'any',
            devices: [
                {
                    notificationId: 'alexa2.0.Echo-Devices.G0911B05926204Q7.Preferences.notificationVolume',
                    volume: 11,
                    notificationOffset: 6,
                    notificationOnly: true
                }
            ]
        },
        {
            name: 'Garten',
            enabled: true,
            statusId: '0_userdata.0.Status_Musikboxen.Garten_Status',
            delayMs: CONFIG.defaultDelayMs,
            trigger: 'any',
            devices: [
                {
                    id: 'alexa2.0.Echo-Devices.G091AA08052203UL.Player.volume',
                    notificationId: 'alexa2.0.Echo-Devices.G091AA08052203UL.Preferences.notificationVolume',
                    volume: 15,
                    notificationOffset: 8
                }
            ]
        }
    ];
    
    const activeTimers = {};
    
    function logInfo(msg) {
        log(`${CONFIG.logPrefix} ${msg}`, 'info');
    }
    
    function logDebug(msg) {
        if (CONFIG.debug) {
            log(`${CONFIG.logPrefix} ${msg}`, 'debug');
        }
    }
    
    function logWarn(msg) {
        log(`${CONFIG.logPrefix} ${msg}`, 'warn');
    }
    
    function logError(msg) {
        log(`${CONFIG.logPrefix} ${msg}`, 'error');
    }
    
    function isTargetStatus(value) {
        if (value === null || value === undefined) return false;
        return String(value).trim() === CONFIG.targetStatus;
    }
    
    function isValidVolume(volume) {
        return typeof volume === 'number' && isFinite(volume) && volume >= 0 && volume <= 100;
    }
    
    function stateExists(id) {
        try {
            return !!getObject(id);
        } catch (e) {
            return false;
        }
    }
    
    function clamp(value, min, max) {
        return Math.max(min, Math.min(max, value));
    }
    
    function getNotificationVolume(volume, notificationOffset) {
        const offset = typeof notificationOffset === 'number'
            ? notificationOffset
            : CONFIG.defaultNotificationOffset;
    
        return clamp(
            volume - offset,
            CONFIG.minNotificationVolume,
            CONFIG.maxNotificationVolume
        );
    }
    
    function clearRoomTimer(roomName) {
        if (activeTimers[roomName]) {
            clearTimeout(activeTimers[roomName]);
            activeTimers[roomName] = null;
            logDebug(`${roomName}: vorhandener Timer gelöscht`);
        }
    }
    
    function setDeviceVolume(device, roomName) {
        if (!device) {
            logWarn(`${roomName}: ungültiges Gerät übersprungen`);
            return;
        }
    
        if (!isValidVolume(device.volume)) {
            logWarn(`${roomName}: ungültige Lautstärke ${device.volume}`);
            return;
        }
    
        if (!device.notificationOnly) {
            if (!device.id) {
                logWarn(`${roomName}: Gerät ohne ID übersprungen`);
                return;
            }
    
            if (!stateExists(device.id)) {
                logWarn(`${roomName}: Datenpunkt nicht gefunden: ${device.id}`);
                return;
            }
    
            try {
                setState(device.id, device.volume);
                logInfo(`${roomName}: Volume ${device.volume} gesetzt -> ${device.id}`);
            } catch (e) {
                logError(`${roomName}: Fehler beim Setzen von ${device.id}: ${e}`);
                return;
            }
        }
    
        if (device.notificationId) {
            if (!stateExists(device.notificationId)) {
                logWarn(`${roomName}: notificationVolume-Datenpunkt nicht gefunden: ${device.notificationId}`);
                return;
            }
    
            const notificationVolume = getNotificationVolume(device.volume, device.notificationOffset);
    
            try {
                setState(device.notificationId, notificationVolume);
                logInfo(`${roomName}: notificationVolume ${notificationVolume} gesetzt -> ${device.notificationId}`);
            } catch (e) {
                logError(`${roomName}: Fehler beim Setzen von ${device.notificationId}: ${e}`);
            }
        }
    }
    
    function resetRoomVolume(room) {
        if (!room || !Array.isArray(room.devices) || room.devices.length === 0) {
            logWarn(`${room && room.name ? room.name : 'Unbekannter Raum'}: keine Geräte konfiguriert`);
            activeTimers[room && room.name ? room.name : 'unknown'] = null;
            return;
        }
    
        room.devices.forEach(device => setDeviceVolume(device, room.name));
        activeTimers[room.name] = null;
    }
    
    function scheduleReset(room) {
        clearRoomTimer(room.name);
    
        const delayMs = typeof room.delayMs === 'number' && room.delayMs >= 0
            ? room.delayMs
            : CONFIG.defaultDelayMs;
    
        if (delayMs === 0) {
            logDebug(`${room.name}: sofortiger Reset`);
            resetRoomVolume(room);
            return;
        }
    
        logDebug(`${room.name}: Reset in ${delayMs} ms geplant`);
    
        activeTimers[room.name] = setTimeout(() => {
            activeTimers[room.name] = null;
            resetRoomVolume(room);
        }, delayMs);
    }
    
    function registerRoom(room) {
        if (!room || !room.name || !room.statusId) {
            logWarn('Ungültige Raum-Konfiguration übersprungen');
            return;
        }
    
        if (!room.enabled) {
            logDebug(`${room.name}: deaktiviert`);
            return;
        }
    
        if (!stateExists(room.statusId)) {
            logWarn(`${room.name}: Status-Datenpunkt nicht gefunden: ${room.statusId}`);
            return;
        }
    
        const trigger = room.trigger || 'ne';
    
        on({ id: room.statusId, change: trigger }, obj => {
            try {
                if (!obj || !obj.state) {
                    logDebug(`${room.name}: Event ohne state erhalten`);
                    return;
                }
    
                const newValue = obj.state.val;
                const oldValue = obj.oldState ? obj.oldState.val : undefined;
                const ack = obj.state.ack;
    
                logDebug(`${room.name}: Event | alt="${oldValue}" neu="${newValue}" ack=${ack}`);
    
                if (!isTargetStatus(newValue)) {
                    logDebug(`${room.name}: Status ist nicht "${CONFIG.targetStatus}", kein Reset`);
                    return;
                }
    
                scheduleReset(room);
            } catch (e) {
                logError(`${room.name}: Fehler im Event-Handler: ${e}`);
            }
        });
    
        logInfo(`${room.name}: Trigger registriert (${trigger}) auf ${room.statusId}`);
    }
    
    rooms.forEach(registerRoom);
    logInfo('Lautstärke-Reset-Skript mit expliziten notificationIds gestartet');
    
    


    es werden noch weitere folgen

    Intel NUC mit Iobroker

    1 Antwort Letzte Antwort
    0
    • H Online
      H Online
      helfi9999
      schrieb am zuletzt editiert von
      #2

      Blink Kamera im Wechsel:

      Vorraussetzung ist der Blink Adapter und das von mir entworfene Skript:

      // ===============================
      // Blink aktive Kamera / Bild / Zeit
      // Version: 1.2.0
      // ===============================
      
      const VERSION = '1.2.0';
      
      const aktiveKamera = '0_userdata.0.Blink.aktive_Kamera';
      const aktivesBild  = '0_userdata.0.Blink.aktives_Bild';
      const aktiveZeit   = '0_userdata.0.Blink.aktive_Zeit';
      
      // Gemeinsame Blink-Sperre für alle Blink-Scripts
      const blinkBusyUntil = '0_userdata.0.Blink.busy_until';
      const blinkBusyInfo  = '0_userdata.0.Blink.busy_info';
      
      const kamera1 = 'blink.0.cameras.706479.live.image_base64';
      const kamera2 = 'blink.0.cameras.1327710.live.image_base64';
      
      const zeit1 = 'blink.0.cameras.706479.live.timestamp';
      const zeit2 = 'blink.0.cameras.1327710.live.timestamp';
      
      const snapshot1 = 'blink.0.cameras.706479.commands.snapshot';
      const snapshot2 = 'blink.0.cameras.1327710.commands.snapshot';
      
      const DEFAULT_KAMERA = '706479';
      
      // Blink ist empfindlich bei zu vielen Befehlen
      const BLINK_SPERRE_MS       = 90000;  // 90 Sekunden Pause nach Snapshot
      const SNAPSHOT_TIMEOUT_MS   = 30000;  // 30 Sekunden auf neuen Timestamp warten
      const DEBOUNCE_MS           = 1500;   // Kamerawechsel entprellen
      
      let letzterTimestamp706479 = '';
      let letzterTimestamp1327710 = '';
      
      let snapshotLaeuft = false;
      let pendingSnapshot = false;
      let debounceTimer = null;
      let waitTimer = null;
      let pollTimer = null;
      
      function ensureState(id, def, common) {
         if (!existsState(id)) {
             createState(id, def, common);
         }
      }
      
      ensureState(aktiveKamera, DEFAULT_KAMERA, {
         type: 'string',
         role: 'text',
         name: 'Aktive Blink Kamera',
         read: true,
         write: true
      });
      
      ensureState(aktivesBild, '', {
         type: 'string',
         role: 'text',
         name: 'Aktives Blink Bild',
         read: true,
         write: true
      });
      
      ensureState(aktiveZeit, '', {
         type: 'string',
         role: 'text',
         name: 'Aktive Blink Zeit',
         read: true,
         write: true
      });
      
      ensureState(blinkBusyUntil, 0, {
         type: 'number',
         role: 'value',
         name: 'Blink busy until timestamp',
         read: true,
         write: true
      });
      
      ensureState(blinkBusyInfo, '', {
         type: 'string',
         role: 'text',
         name: 'Blink busy info',
         read: true,
         write: true
      });
      
      function getBusyUntil() {
         const state = getState(blinkBusyUntil);
      
         if (!state || state.val === null || state.val === undefined) {
             return 0;
         }
      
         const value = Number(state.val);
         return isNaN(value) ? 0 : value;
      }
      
      function istBlinkBusy() {
         return Date.now() < getBusyUntil();
      }
      
      function getRestzeitMs() {
         return Math.max(0, getBusyUntil() - Date.now());
      }
      
      function setBlinkBusy(ms, info) {
         const until = Date.now() + ms;
      
         setState(blinkBusyUntil, until, true);
         setState(blinkBusyInfo, info, true);
      
         log(`[v${VERSION}] Blink gesperrt für ${Math.ceil(ms / 1000)}s: ${info}`, 'info');
      }
      
      function getKameraWert() {
         const state = getState(aktiveKamera);
      
         if (!state || state.val === null || state.val === undefined || state.val === '') {
             return DEFAULT_KAMERA;
         }
      
         const cam = String(state.val).replace(/"/g, '');
      
         if (cam !== '706479' && cam !== '1327710') {
             log(`[v${VERSION}] Blink: Ungültige Kamera "${cam}", verwende ${DEFAULT_KAMERA}`, 'warn');
             return DEFAULT_KAMERA;
         }
      
         return cam;
      }
      
      function getBildQuelle(cam) {
         return cam === '1327710' ? kamera2 : kamera1;
      }
      
      function getZeitQuelle(cam) {
         return cam === '1327710' ? zeit2 : zeit1;
      }
      
      function getSnapshotQuelle(cam) {
         return cam === '1327710' ? snapshot2 : snapshot1;
      }
      
      function getLetztenTimestamp(cam) {
         return cam === '1327710' ? letzterTimestamp1327710 : letzterTimestamp706479;
      }
      
      function setLetztenTimestamp(cam, value) {
         if (cam === '1327710') {
             letzterTimestamp1327710 = value;
         } else {
             letzterTimestamp706479 = value;
         }
      }
      
      function formatZeitBerlin(timestamp) {
         const datum = new Date(timestamp);
      
         if (isNaN(datum.getTime())) {
             return String(timestamp);
         }
      
         return datum.toLocaleString('de-DE', {
             timeZone: 'Europe/Berlin',
             day: '2-digit',
             month: '2-digit',
             year: 'numeric',
             hour: '2-digit',
             minute: '2-digit'
         }).replace(',', '');
      }
      
      function updateBild(cam) {
         const quelle = getBildQuelle(cam);
         const bildState = getState(quelle);
      
         if (!bildState || !bildState.val) {
             log(`[v${VERSION}] Blink: Kein Bild vorhanden von ${quelle}`, 'warn');
             return false;
         }
      
         if (getKameraWert() === cam) {
             setState(aktivesBild, String(bildState.val), true);
         }
      
         return true;
      }
      
      function updateZeit(cam) {
         const quelle = getZeitQuelle(cam);
         const zeitState = getState(quelle);
      
         if (!zeitState || !zeitState.val) {
             log(`[v${VERSION}] Blink: Keine Zeit vorhanden von ${quelle}`, 'warn');
             return false;
         }
      
         const formatiert = formatZeitBerlin(zeitState.val);
      
         if (getKameraWert() === cam) {
             setState(aktiveZeit, formatiert, true);
         }
      
         return true;
      }
      
      function updateAlles(cam) {
         cam = cam || getKameraWert();
      
         updateBild(cam);
         updateZeit(cam);
      }
      
      function snapshotAusloesen(cam) {
         const snapshot = getSnapshotQuelle(cam);
      
         log(`[v${VERSION}] Blink: Snapshot auslösen: ${cam}`, 'info');
      
         // Befehl aktiv auslösen
         setState(snapshot, true, false);
      }
      
      function planeSnapshotNachSperre() {
         if (waitTimer) {
             clearTimeout(waitTimer);
         }
      
         const wait = getRestzeitMs() + 1000;
      
         log(`[v${VERSION}] Blink ist busy. Snapshot wird in ${Math.ceil(wait / 1000)}s erneut versucht.`, 'warn');
      
         waitTimer = setTimeout(function () {
             waitTimer = null;
      
             if (pendingSnapshot && !snapshotLaeuft) {
                 pendingSnapshot = false;
                 updateNachSnapshot();
             }
         }, wait);
      }
      
      function updateNachSnapshot() {
         const cam = getKameraWert();
      
         if (snapshotLaeuft) {
             pendingSnapshot = true;
             log(`[v${VERSION}] Blink: Snapshot läuft bereits, neuer Wunsch wird gemerkt: ${cam}`, 'warn');
             return;
         }
      
         if (istBlinkBusy()) {
             pendingSnapshot = true;
             planeSnapshotNachSperre();
             return;
         }
      
         snapshotLaeuft = true;
         pendingSnapshot = false;
      
         const zeitQuelle = getZeitQuelle(cam);
         const alterTimestampState = getState(zeitQuelle);
         const alterTimestamp = alterTimestampState && alterTimestampState.val ? String(alterTimestampState.val) : '';
      
         setLetztenTimestamp(cam, alterTimestamp);
      
         setBlinkBusy(BLINK_SPERRE_MS, `Snapshot Kamera ${cam}`);
         snapshotAusloesen(cam);
      
         let vergangen = 0;
      
         if (pollTimer) {
             clearInterval(pollTimer);
             pollTimer = null;
         }
      
         pollTimer = setInterval(function () {
             vergangen += 1000;
      
             const aktuelleCam = getKameraWert();
             const neuerTimestampState = getState(zeitQuelle);
             const neuerTimestamp = neuerTimestampState && neuerTimestampState.val ? String(neuerTimestampState.val) : '';
      
             if (neuerTimestamp && neuerTimestamp !== getLetztenTimestamp(cam)) {
                 setLetztenTimestamp(cam, neuerTimestamp);
      
                 if (aktuelleCam === cam) {
                     updateAlles(cam);
                 }
      
                 clearInterval(pollTimer);
                 pollTimer = null;
                 snapshotLaeuft = false;
      
                 log(`[v${VERSION}] Blink: Snapshot erfolgreich aktualisiert: ${cam}`, 'info');
      
                 if (pendingSnapshot) {
                     planeSnapshotNachSperre();
                 }
      
                 return;
             }
      
             if (vergangen >= SNAPSHOT_TIMEOUT_MS) {
                 if (aktuelleCam === cam) {
                     updateAlles(cam);
                 }
      
                 clearInterval(pollTimer);
                 pollTimer = null;
                 snapshotLaeuft = false;
      
                 log(`[v${VERSION}] Blink: Snapshot hat nach ${SNAPSHOT_TIMEOUT_MS / 1000}s keinen neuen Timestamp geliefert: ${cam}`, 'warn');
      
                 if (pendingSnapshot) {
                     planeSnapshotNachSperre();
                 }
             }
         }, 1000);
      }
      
      function kameraWechselEntprellt() {
         if (debounceTimer) {
             clearTimeout(debounceTimer);
         }
      
         debounceTimer = setTimeout(function () {
             debounceTimer = null;
             updateNachSnapshot();
         }, DEBOUNCE_MS);
      }
      
      // Nur bei echter Änderung reagieren
      on({ id: aktiveKamera, change: 'ne' }, function () {
         kameraWechselEntprellt();
      });
      
      on({ id: kamera1, change: 'ne' }, function () {
         if (getKameraWert() === '706479') {
             updateBild('706479');
         }
      });
      
      on({ id: kamera2, change: 'ne' }, function () {
         if (getKameraWert() === '1327710') {
             updateBild('1327710');
         }
      });
      
      on({ id: zeit1, change: 'ne' }, function () {
         if (getKameraWert() === '706479') {
             updateZeit('706479');
         }
      });
      
      on({ id: zeit2, change: 'ne' }, function () {
         if (getKameraWert() === '1327710') {
             updateZeit('1327710');
         }
      });
      
      log(`[v${VERSION}] Blink aktive Kamera Script gestartet`, 'info');
      
      setTimeout(function () {
         updateAlles();
      }, 1000);
      


      Habe an der Terrassentür ein Türkontakt wenn die Tür auf ist nimmt die Kamera im Garten nicht auf das gleiche ist auch bei der Haustür.

      f0ad6a37-62ee-486d-86c0-a1ba28cb2530-image.jpeg

      Intel NUC mit Iobroker

      1 Antwort Letzte Antwort
      0
      • H Online
        H Online
        helfi9999
        schrieb am zuletzt editiert von helfi9999
        #3

        Ecovacs Deebot interaktive Karte:


        4076a06c-81d3-4430-89c6-6c3852c56c25-image.jpeg

        Habe eine Skript entwickelt das die Karte einsetzt und die Fahrt des Saugers mit aufzeichnet. Man kann auch über die Karte die Räume auswählen.

        Für das Widget der Karte muss man folgenden Wert eintragen:

        <!-- LUNA VIS HTML v1.1 --> <div style="position:relative;width:540px;height:231px;overflow:hidden;background:none;border:none;">    <img     src="http://192.168.178.59:8082/files/ecovacs-deebot.2/lastestCleaningMapImage.png"     style="position:absolute;left:0;top:0;width:540px;height:231px;z-index:1;"   >    <svg viewBox="0 0 540 231"        style="position:absolute;left:0;top:0;width:540px;height:231px;z-index:2;pointer-events:auto;">      <polyline       points="{0_userdata.0.Luna.Map.trailPoints}"       fill="none"       stroke="#ff3b30"       stroke-width="2"       opacity="0.8"       style="pointer-events:none;"     />      <polygon fill="orange"       fill-opacity="{0_userdata.0.Luna.Map.room0Opacity}"       style="cursor:pointer;"       onclick="         const mapId = vis.states['0_userdata.0.Luna.Map.mapId.val'] || '1245233875';         const id = 'ecovacs-deebot.2.map.' + mapId + '.spotAreas.0.markForNextSpotAreaCleaning';         vis.setValue(id, !(vis.states[id + '.val']));       "       points="0,231 2.5,196.9 7.4,192.1 2.5,187.2 2.5,119.2 4.9,116.7 2.5,68.1 7.4,60.8 7.4,4.9 22.2,4.9 24.7,26.8 34.6,34 44.4,34 44.4,114.3 51.8,121.6 140.7,121.6 145.5,116.7 145.5,36.5 162.9,34 170.3,24.3 177.7,21.9 180.1,36.5 187.5,43.8 187.5,97.2 180.1,104.5 180.1,116.7 185.1,121.6 185.1,153.2 180.1,153.2 170.3,162.9 155.4,170.2 165.3,179.9 177.7,170.2 177.7,175 170.3,179.9 177.7,187.2 175.2,206.6 172.8,204.2 19.7,204.2 14.8,209.1 7.4,231 0,231" />      <polygon fill="lime"       fill-opacity="{0_userdata.0.Luna.Map.room1Opacity}"       style="cursor:pointer;"       onclick="         const mapId = vis.states['0_userdata.0.Luna.Map.mapId.val'] || '1245233875';         const id = 'ecovacs-deebot.2.map.' + mapId + '.spotAreas.1.markForNextSpotAreaCleaning';         vis.setValue(id, !(vis.states[id + '.val']));       "       points="190.0,153.2 190.0,121.6 197.5,116.7 271.4,116.7 278.9,99.6 293.7,104.5 308.5,104.5 313.4,111.8 313.4,126.5 323.3,131.3 318.4,153.2 311.0,158.1 313.4,221.9 308.5,231 202.4,231 202.4,175 214.7,187.2 219.7,187.2 246.8,175 254.2,162.9 246.8,155.6 244.3,158.1 209.8,155.6 204.9,160.5 192.5,153.2 190.0,153.2" />      <polygon fill="violet"       fill-opacity="{0_userdata.0.Luna.Map.room2Opacity}"       style="cursor:pointer;"       onclick="         const mapId = vis.states['0_userdata.0.Luna.Map.mapId.val'] || '1245233875';         const id = 'ecovacs-deebot.2.map.' + mapId + '.spotAreas.2.markForNextSpotAreaCleaning';         vis.setValue(id, !(vis.states[id + '.val']));       "       points="273.8,63.2 276.3,7.3 288.7,9.7 293.7,26.8 362.9,26.8 370.3,19.5 372.8,7.3 407.3,7.3 414.7,14.6 409.7,19.5 409.7,34 417.2,41.3 429.5,41.3 439.4,55.9 540,55.9 537.5,99.6 443.8,99.6 433.9,87.5 429.5,92.4 417.2,87.5 407.3,99.6 375.2,99.6 367.8,85.1 360.4,89.9 325.8,85.1 313.4,99.6 293.7,99.6 281.3,97.2 283.8,63.2 278.9,58.3 273.8,63.2" />      <polygon fill="cyan"       fill-opacity="{0_userdata.0.Luna.Map.room3Opacity}"       style="cursor:pointer;"       onclick="         const mapId = vis.states['0_userdata.0.Luna.Map.mapId.val'] || '1245233875';         const id = 'ecovacs-deebot.2.map.' + mapId + '.spotAreas.3.markForNextSpotAreaCleaning';         vis.setValue(id, !(vis.states[id + '.val']));       "       points="323.3,155.6 328.2,133.7 362.9,133.7 370.3,119.2 375.2,116.7 464.1,116.7 469.0,121.6 476.4,116.7 540,116.7 542.5,119.2 542.5,143.5 513.4,148.3 513.4,187.2 518.3,192.1 542.5,192.1 540,201.8 488.7,201.8 483.8,196.9 466.5,196.9 461.5,201.8 439.4,201.8 432.0,209.1 434.4,221.2 427.0,226.1 422.1,226.1 422.1,218.8 414.7,211.5 404.8,218.8 404.8,226.1 390.0,226.1 382.6,221.2 382.6,189.6 375.2,182.3 335.6,182.3 333.2,155.6 323.3,155.6" />    </svg>    <div style="     position:absolute;     left:{0_userdata.0.Luna.Map.pixelXcss};     top:{0_userdata.0.Luna.Map.pixelYcss};     transform:{0_userdata.0.Luna.Map.rotateCss};     width:18px;height:18px;     z-index:9999;     pointer-events:none;   ">     <img src="https://api-app.dc-eu.ww.ecouser.net/api/pim/file/get/5ba4a2cb6c2f120001c32839"          style="width:18px;height:18px;">   </div>    <span style="display:none;">     {0_userdata.0.Luna.Map.mapId}     {0_userdata.0.Luna.Map.pixelXcss}     {0_userdata.0.Luna.Map.pixelYcss}     {0_userdata.0.Luna.Map.rotateCss}     {0_userdata.0.Luna.Map.trailPoints}     {0_userdata.0.Luna.Map.room0Opacity}     {0_userdata.0.Luna.Map.room1Opacity}     {0_userdata.0.Luna.Map.room2Opacity}     {0_userdata.0.Luna.Map.room3Opacity}   </span>  </div>
        

        Demensprechend muss man seine Daten ersetzen sowie beim Hauptskript auch:

        /*************************************************
        * SAUGROBOTER-STEUERUNG LUNA
        * Version: 1.2.1
        * Automatik + Alexa + VIS + Telegram + Karte/Trail
        * Ohne zusätzliche ioBroker-Logs
        *************************************************/
        
        const VERSION = '1.2.1';
        
        const ROBOT_NAME = 'Luna';
        
        const TELEGRAM_INSTANCE = 'telegram.0';
        const TELEGRAM_USER_ID  = '1365513925';
        const TELEGRAM_REQUEST  = `${TELEGRAM_INSTANCE}.communicate.request`;
        
        const STATE_KERSTIN = 'fb-checkpresence.0.Kerstin-Handy.presence';
        const STATE_THOMAS  = 'fb-checkpresence.0.Thomas-IPhone.presence';
        const STATE_FEIERTAG = 'feiertage.0.heute.boolean';
        
        const STATE_BASE                = '0_userdata.0.Luna';
        const STATE_SAUGEN_FREIGABE     = `${STATE_BASE}.saugen`;
        const STATE_STATUS_TEXT         = `${STATE_BASE}.statusText`;
        const STATE_LAST_RUN            = `${STATE_BASE}.lastRun`;
        const STATE_LAST_DECISION       = `${STATE_BASE}.lastDecision`;
        const STATE_MANUAL_START        = `${STATE_BASE}.manualStart`;
        const STATE_MANUAL_RESET        = `${STATE_BASE}.manualReset`;
        const STATE_POSITION_TEXT       = `${STATE_BASE}.Position`;
        const STATE_WAS_SAUGEN          = `${STATE_BASE}.Was_saugen`;
        const STATE_LAST_HOLIDAY_NOTIFY = `${STATE_BASE}.lastHolidayNotification`;
        const STATE_LAST_CLEANED_ROOMS  = `${STATE_BASE}.lastCleanedRooms`;
        
        const ALEXA_STATE = 'alexa2.0.History.summary';
        const ROBOT_LUNA  = 'luna';
        const ROBOT_SKY   = 'sky';
        
        const DEEBOT_PREFIX   = 'ecovacs-deebot.2';
        const DEEBOT_AREA     = `${DEEBOT_PREFIX}.control.spotArea`;
        const DEEBOT_CHARGE   = `${DEEBOT_PREFIX}.info.chargestatus`;
        const DEEBOT_POSITION = `${DEEBOT_PREFIX}.map.deebotPositionCurrentSpotAreaName`;
        
        const DEFAULT_MAP_ID = '1245233875';
        
        const DEEBOT_POS_STATE    = `${DEEBOT_PREFIX}.map.deebotPosition`;
        const DEEBOT_STATUS_STATE = `${DEEBOT_PREFIX}.info.deviceStatus`;
        
        const MAP_BASE      = `${STATE_BASE}.Map`;
        const STATE_MAP_ID  = `${MAP_BASE}.mapId`;
        const STATE_PIXEL_X = `${MAP_BASE}.pixelXcss`;
        const STATE_PIXEL_Y = `${MAP_BASE}.pixelYcss`;
        const STATE_ROTATE  = `${MAP_BASE}.rotateCss`;
        const STATE_TRAIL   = `${MAP_BASE}.trailPoints`;
        
        const minX = -250;
        const maxX = 10700;
        const minY = -4350;
        const maxY = 400;
        
        const imgW = 540;
        const imgH = 231;
        
        const CRON_ENABLE      = '17 19 * * *';
        const CRON_CHECK       = '*/5 9-12 * * 1-5';
        const CRON_RESET_DAILY = '0 0 * * *';
        
        const RESET_AFTER_CHARGING_MS = 10000;
        const DEBOUNCE_MS             = 10000;
        
        const SETTINGS = {
           SEND_TELEGRAM: true,
           SKIP_ON_HOLIDAY: true
        };
        
        const DAYS = {
           MONDAY: 1,
           WEDNESDAY: 3,
           FRIDAY: 5
        };
        
        const ROOMS = [
           {
               id: 0,
               name: 'Schlafzimmer',
               mapLetter: 'A',
               roomState: `${STATE_BASE}.Schlafzimmer`,
               spotAlias: 'alias.0.Luna.Spot0.ACTUAL',
               markAlias: 'alias.0.Luna.Mark0.ACTUAL',
               aliases: ['schlafzimmer']
           },
           {
               id: 1,
               name: 'Flur',
               mapLetter: 'B',
               roomState: `${STATE_BASE}.Flur`,
               spotAlias: 'alias.0.Luna.Spot1.ACTUAL',
               markAlias: 'alias.0.Luna.Mark1.ACTUAL',
               aliases: ['flur']
           },
           {
               id: 2,
               name: 'Kinderzimmer',
               mapLetter: 'C',
               roomState: `${STATE_BASE}.Kinderzimmer`,
               spotAlias: 'alias.0.Luna.Spot2.ACTUAL',
               markAlias: 'alias.0.Luna.Mark2.ACTUAL',
               aliases: ['kinderzimmer']
           },
           {
               id: 3,
               name: 'Badezimmer',
               mapLetter: 'D',
               roomState: `${STATE_BASE}.Badezimmer`,
               spotAlias: 'alias.0.Luna.Spot3.ACTUAL',
               markAlias: 'alias.0.Luna.Mark3.ACTUAL',
               aliases: ['badezimmer', 'bad']
           }
        ];
        
        const ALL_ALIASES = ['obergeschoss', 'oben', 'alles'];
        
        const CANCEL_ALIASES = [
           'stopp',
           'stop',
           'abbrechen',
           'abbruch',
           'nichts',
           'doch nicht'
        ];
        
        const ENABLE_ALIASES = [
           'freigeben',
           'freigabe',
           'darf saugen',
           'saugen erlauben',
           'automatik an',
           'automatik aktivieren'
        ];
        
        const DISABLE_ALIASES = [
           'nicht saugen',
           'freigabe aus',
           'freigabe deaktivieren',
           'saugen verbieten',
           'automatik aus',
           'automatik deaktivieren'
        ];
        
        const AUTOMATIC_ROOM_ID = 0;
        const AUTOMATIC_DAYS = [DAYS.MONDAY, DAYS.WEDNESDAY, DAYS.FRIDAY];
        
        let lastActionText = '';
        let lastActionTime = 0;
        let lastSpotArea = '';
        let resetTimer = null;
        
        let trail = [];
        let wasCleaning = false;
        let chargingNotified = false;
        
        function notify(text) {
           if (!SETTINGS.SEND_TELEGRAM) return;
        
           sendTo(TELEGRAM_INSTANCE, 'send', {
               chatId: TELEGRAM_USER_ID,
               text: `${ROBOT_NAME}: ${text}`
           });
        }
        
        function safeExistsState(id) {
           try {
               return existsState(id);
           } catch (e) {
               return false;
           }
        }
        
        function getBoolState(id, fallback = false) {
           try {
               if (!safeExistsState(id)) return fallback;
               const s = getState(id);
               if (!s || s.val === null || s.val === undefined) return fallback;
               return !!s.val;
           } catch (e) {
               return fallback;
           }
        }
        
        function getStringState(id, fallback = '') {
           try {
               if (!safeExistsState(id)) return fallback;
               const s = getState(id);
               if (!s || s.val === null || s.val === undefined) return fallback;
               return String(s.val);
           } catch (e) {
               return fallback;
           }
        }
        
        function normalize(text) {
           return String(text || '')
               .toLowerCase()
               .normalize('NFD').replace(/[\u0300-\u036f]/g, '')
               .replace(/[^\w\s]/g, ' ')
               .replace(/\s+/g, ' ')
               .trim();
        }
        
        function includesAlias(text, aliases) {
           return aliases.some(alias => text.includes(normalize(alias)));
        }
        
        function getTimestamp() {
           return formatDate(new Date(), 'YYYY-MM-DD hh:mm:ss');
        }
        
        function setStatus(text) {
           setState(STATE_STATUS_TEXT, text, true);
        }
        
        function setDecision(text) {
           setState(STATE_LAST_DECISION, text, true);
        }
        
        function logAction(text) {
           const now = Date.now();
        
           if (text === lastActionText && now - lastActionTime < DEBOUNCE_MS) {
               return;
           }
        
           lastActionText = text;
           lastActionTime = now;
        
           setState(STATE_WAS_SAUGEN, text, true);
           setStatus(text);
           notify(text);
        }
        
        function isSomeoneHome() {
           return getBoolState(STATE_KERSTIN) || getBoolState(STATE_THOMAS);
        }
        
        function isCleaningEnabled() {
           return getBoolState(STATE_SAUGEN_FREIGABE);
        }
        
        function setCleaningEnabled(value) {
           setState(STATE_SAUGEN_FREIGABE, value, true);
        }
        
        function isHoliday() {
           return getBoolState(STATE_FEIERTAG, false);
        }
        
        function getWeekday() {
           return new Date().getDay();
        }
        
        function isSameDay(ts1, ts2) {
           if (!ts1 || !ts2) return false;
           return new Date(ts1).toDateString() === new Date(ts2).toDateString();
        }
        
        function sendHolidayNotificationOncePerDay(message) {
           const now = new Date();
           const lastNotify = getStringState(STATE_LAST_HOLIDAY_NOTIFY, '');
        
           if (!isSameDay(now, lastNotify)) {
               notify(message);
               setState(STATE_LAST_HOLIDAY_NOTIFY, now.toISOString(), true);
           }
        }
        
        function getRoomById(id) {
           return ROOMS.find(room => String(room.id) === String(id));
        }
        
        function getRoomByLetter(letter) {
           return ROOMS.find(room => room.mapLetter === String(letter));
        }
        
        function getRoomNamesBySpotArea(spotArea) {
           if (spotArea === '0,1,2,3') {
               return ROOMS.map(room => room.name);
           }
        
           const room = getRoomById(spotArea);
           return room ? [room.name] : [];
        }
        
        function roomListText(roomNames) {
           if (!roomNames || roomNames.length === 0) return 'keine Räume';
           return roomNames.join(', ');
        }
        
        function clearTimer(timer) {
           if (timer) clearTimeout(timer);
        }
        
        function getCurrentMapId() {
           return getStringState(STATE_MAP_ID, DEFAULT_MAP_ID);
        }
        
        function getMapMarkState(roomId) {
           return `${DEEBOT_PREFIX}.map.${getCurrentMapId()}.spotAreas.${roomId}.markForNextSpotAreaCleaning`;
        }
        
        function clearAllMarks() {
           for (const room of ROOMS) {
               setState(room.markAlias, false);
           }
        
           for (const room of ROOMS) {
               const mapMark = getMapMarkState(room.id);
               if (safeExistsState(mapMark)) {
                   setState(mapMark, false);
               }
           }
        }
        
        function resetRoomStates() {
           for (const room of ROOMS) {
               setState(room.roomState, false, true);
           }
        }
        
        function resetSelection(reason) {
           clearAllMarks();
           resetRoomStates();
           setState(DEEBOT_AREA, ' ', true);
           lastSpotArea = '';
           setDecision(`Auswahl zurückgesetzt: ${reason}`);
        }
        
        function saveCleanedRooms(roomNames) {
           const text = roomListText(roomNames);
           setState(STATE_LAST_CLEANED_ROOMS, text, true);
           setState(STATE_WAS_SAUGEN, text, true);
        }
        
        function startRoomCleaning(room, source) {
           if (!room) return;
        
           chargingNotified = false;
        
           setState(room.spotAlias, true);
        
           const cleanedRooms = [room.name];
           saveCleanedRooms(cleanedRooms);
        
           setState(STATE_LAST_RUN, getTimestamp(), true);
           setDecision(`${source}: ${room.name} gestartet`);
           logAction(`${source}: ${room.name} wird gesaugt`);
        }
        
        function startAllCleaning(source) {
           chargingNotified = false;
        
           setState(DEEBOT_AREA, '0,1,2,3');
        
           const cleanedRooms = ROOMS.map(room => room.name);
           saveCleanedRooms(cleanedRooms);
        
           setState(STATE_LAST_RUN, getTimestamp(), true);
           setDecision(`${source}: Obergeschoss gestartet`);
           logAction(`${source}: Obergeschoss wird gesaugt: ${roomListText(cleanedRooms)}`);
        }
        
        /*************************************************
        * TELEGRAM STATUS
        *************************************************/
        
        function getCleaningStateText() {
           const deviceStatus = getStringState(DEEBOT_STATUS_STATE, '').toLowerCase();
           const chargeStatus = getStringState(DEEBOT_CHARGE, '').toLowerCase();
        
           const isCleaning =
               deviceStatus.includes('clean') ||
               deviceStatus.includes('spot') ||
               deviceStatus.includes('auto') ||
               deviceStatus.includes('edge');
        
           if (isCleaning) return 'Ja';
           if (chargeStatus === 'charging') return 'Nein, Luna steht auf der Ladestation';
        
           return 'Unbekannt';
        }
        
        function sendLunaStatus() {
           const saugtWas = getStringState(STATE_WAS_SAUGEN, 'nichts bekannt');
           const lastCleaned = getStringState(STATE_LAST_CLEANED_ROOMS, 'nichts bekannt');
           const freigabe = isCleaningEnabled() ? 'Ja' : 'Nein';
           const position = getStringState(STATE_POSITION_TEXT, 'Unbekannt');
           const statusText = getStringState(STATE_STATUS_TEXT, '-');
           const letzteEntscheidung = getStringState(STATE_LAST_DECISION, '-');
           const lastRun = getStringState(STATE_LAST_RUN, '-');
           const mapId = getCurrentMapId();
           const saugtGerade = getCleaningStateText();
        
           notify(
               `Status:\n` +
               `Saugt gerade: ${saugtGerade}\n` +
               `Was: ${saugtWas}\n` +
               `Zuletzt gereinigte Räume: ${lastCleaned}\n` +
               `Freigabe nächster Automatiklauf: ${freigabe}\n` +
               `Position: ${position}\n` +
               `Letzter Lauf: ${lastRun}\n` +
               `Status VIS: ${statusText}\n` +
               `Letzte Entscheidung: ${letzteEntscheidung}\n` +
               `Map-ID: ${mapId}`
           );
        }
        
        on({ id: TELEGRAM_REQUEST, change: 'any' }, obj => {
           if (!obj || !obj.state || !obj.state.val) return;
        
           let raw = String(obj.state.val);
        
           raw = raw.replace(/^\[[^\]]+\]/, '').trim();
        
           const text = normalize(raw);
        
           const isLunaStatusRequest =
               text.includes('was macht luna') ||
               text.includes('luna status') ||
               text.includes('status luna') ||
               text.includes('luna freigabe') ||
               text.includes('luna saugt') ||
               text.includes('luna info') ||
               text === 'luna';
        
           if (!isLunaStatusRequest) {
               return;
           }
        
           sendLunaStatus();
        });
        
        /*************************************************
        * VIS KARTE / POSITION / TRAIL
        *************************************************/
        
        function toPixel(x, y) {
           return {
               x: ((x - minX) / (maxX - minX)) * imgW,
               y: imgH - ((y - minY) / (maxY - minY)) * imgH
           };
        }
        
        function parseBotPosition(value) {
           if (!value) return null;
        
           const p = String(value).split(',').map(Number);
           if (p.length < 3 || p.some(isNaN)) return null;
        
           return {
               x: p[0],
               y: p[1],
               angle: p[2]
           };
        }
        
        function updateBotOnMap(value) {
           const pos = parseBotPosition(value);
           if (!pos) return;
        
           const pixel = toPixel(pos.x, pos.y);
        
           setState(STATE_PIXEL_X, pixel.x.toFixed(1) + 'px', true);
           setState(STATE_PIXEL_Y, pixel.y.toFixed(1) + 'px', true);
           setState(STATE_ROTATE, `translate(-50%,-50%) rotate(${pos.angle}deg)`, true);
        
           const last = trail[trail.length - 1];
        
           if (!last || Math.abs(last.x - pixel.x) > 1 || Math.abs(last.y - pixel.y) > 1) {
               trail.push(pixel);
        
               if (trail.length > 500) {
                   trail.shift();
               }
        
               const points = trail
                   .map(pt => `${pt.x.toFixed(1)},${pt.y.toFixed(1)}`)
                   .join(' ');
        
               setState(STATE_TRAIL, points, true);
           }
        }
        
        function resetTrail() {
           trail = [];
           setState(STATE_TRAIL, '', true);
        }
        
        function checkResetTrailByStatus(status) {
           if (!status) return;
        
           const s = String(status).toLowerCase();
        
           const isCleaning =
               s.includes('clean') ||
               s.includes('spot') ||
               s.includes('auto') ||
               s.includes('edge');
        
           if (isCleaning && !wasCleaning) {
               resetTrail();
               wasCleaning = true;
               chargingNotified = false;
           }
        
           const isFinished =
               s.includes('charging') ||
               s.includes('charge') ||
               s.includes('dock') ||
               s.includes('idle') ||
               s.includes('stop') ||
               s.includes('finish') ||
               s.includes('complete') ||
               s.includes('standby');
        
           if (isFinished) {
               wasCleaning = false;
           }
        }
        
        function updateRoomOpacity(roomNumber, value) {
           const selected = value === true || value === 'true' || value === 1 || value === '1';
           const opacity = selected ? 0.75 : 0.25;
        
           setState(`${MAP_BASE}.room${roomNumber}Opacity`, opacity, true);
        }
        
        /*************************************************
        * STATES AUTOMATISCH ANLEGEN
        *************************************************/
        
        function createUserStates() {
           createState(STATE_SAUGEN_FREIGABE, false, {
               name: 'Saugfreigabe für nächsten Reinigungslauf',
               type: 'boolean',
               role: 'switch.enable',
               read: true,
               write: true,
               def: false
           });
        
           createState(STATE_STATUS_TEXT, '', {
               name: 'Statusanzeige Luna',
               type: 'string',
               role: 'text',
               read: true,
               write: false,
               def: ''
           });
        
           createState(STATE_LAST_RUN, '', {
               name: 'Letzter Luna Reinigungslauf',
               type: 'string',
               role: 'text',
               read: true,
               write: false,
               def: ''
           });
        
           createState(STATE_LAST_DECISION, '', {
               name: 'Letzte Luna Entscheidung',
               type: 'string',
               role: 'text',
               read: true,
               write: false,
               def: ''
           });
        
           createState(STATE_MANUAL_START, false, {
               name: 'Manueller Luna Start per VIS',
               type: 'boolean',
               role: 'button',
               read: true,
               write: true,
               def: false
           });
        
           createState(STATE_MANUAL_RESET, false, {
               name: 'Manueller Luna Reset',
               type: 'boolean',
               role: 'button',
               read: true,
               write: true,
               def: false
           });
        
           createState(STATE_POSITION_TEXT, '', {
               name: 'Aktuelle Luna Position',
               type: 'string',
               role: 'text',
               read: true,
               write: false,
               def: ''
           });
        
           createState(STATE_WAS_SAUGEN, '', {
               name: 'Was saugt Luna',
               type: 'string',
               role: 'text',
               read: true,
               write: false,
               def: ''
           });
        
           createState(STATE_LAST_HOLIDAY_NOTIFY, '', {
               name: 'Letzte Feiertags-Benachrichtigung Luna',
               type: 'string',
               role: 'text',
               read: true,
               write: false,
               def: ''
           });
        
           createState(STATE_LAST_CLEANED_ROOMS, '', {
               name: 'Zuletzt gesaugte Räume Luna',
               type: 'string',
               role: 'text',
               read: true,
               write: false,
               def: ''
           });
        
           for (const room of ROOMS) {
               createState(room.roomState, false, {
                   name: `Luna Raum aktiv: ${room.name}`,
                   type: 'boolean',
                   role: 'indicator',
                   read: true,
                   write: false,
                   def: false
               });
           }
        
           createState(STATE_MAP_ID, DEFAULT_MAP_ID, {
               name: 'Luna aktuelle Map ID',
               type: 'string',
               role: 'text',
               read: true,
               write: true,
               def: DEFAULT_MAP_ID
           });
        
           createState(STATE_PIXEL_X, '0px', {
               name: 'Luna Karte X Position CSS',
               type: 'string',
               role: 'text',
               read: true,
               write: true,
               def: '0px'
           });
        
           createState(STATE_PIXEL_Y, '0px', {
               name: 'Luna Karte Y Position CSS',
               type: 'string',
               role: 'text',
               read: true,
               write: true,
               def: '0px'
           });
        
           createState(STATE_ROTATE, 'translate(-50%,-50%) rotate(0deg)', {
               name: 'Luna Karte Rotation CSS',
               type: 'string',
               role: 'text',
               read: true,
               write: true,
               def: 'translate(-50%,-50%) rotate(0deg)'
           });
        
           createState(STATE_TRAIL, '', {
               name: 'Luna Karte Fahrspur Punkte',
               type: 'string',
               role: 'text',
               read: true,
               write: true,
               def: ''
           });
        
           for (const room of ROOMS) {
               createState(`${MAP_BASE}.room${room.id}Opacity`, 0.25, {
                   name: `Luna Karte Raum ${room.id} Deckkraft`,
                   type: 'number',
                   role: 'value',
                   read: true,
                   write: true,
                   def: 0.25
               });
           }
        }
        
        /*************************************************
        * AUTOMATIK
        *************************************************/
        
        function shouldCleanNow() {
           if (!isCleaningEnabled()) {
               setDecision('Abbruch: Freigabe ist false');
               return false;
           }
        
           if (!AUTOMATIC_DAYS.includes(getWeekday())) {
               setDecision('Heute ist kein Luna Reinigungstag');
               return false;
           }
        
           if (SETTINGS.SKIP_ON_HOLIDAY && isHoliday()) {
               const room = getRoomById(AUTOMATIC_ROOM_ID);
               const roomsText = room ? room.name : 'Automatikraum';
        
               const msg = `Heute keine Reinigung wegen Feiertag. Geplant gewesen wäre: ${roomsText}.`;
        
               setStatus(msg);
               setDecision('Abbruch: Feiertag');
               sendHolidayNotificationOncePerDay(msg);
               return false;
           }
        
           if (isSomeoneHome()) {
               setDecision('Abbruch: jemand ist zuhause');
               return false;
           }
        
           return true;
        }
        
        function runAutomaticCleaning() {
           if (!shouldCleanNow()) return;
        
           const room = getRoomById(AUTOMATIC_ROOM_ID);
           if (!room) {
               setDecision('Abbruch: Automatikraum nicht gefunden');
               return;
           }
        
           startRoomCleaning(room, 'Automatik');
           setCleaningEnabled(false);
        }
        
        function runManualCleaning() {
           const room = getRoomById(AUTOMATIC_ROOM_ID);
           startRoomCleaning(room, 'Manueller Start');
        }
        
        /*************************************************
        * ALEXA
        *************************************************/
        
        on({ id: ALEXA_STATE, change: 'any' }, obj => {
           const raw = obj.state.val;
           const spoken = normalize(raw);
        
           if (!spoken) return;
        
           if (spoken.includes(ROBOT_SKY)) {
               setDecision(`Sky-Befehl ignoriert: ${raw}`);
               return;
           }
        
           if (!spoken.includes(ROBOT_LUNA)) {
               return;
           }
        
           if (includesAlias(spoken, ENABLE_ALIASES)) {
               setCleaningEnabled(true);
               setStatus('Freigabe per Alexa aktiviert');
               setDecision('Freigabe per Alexa gesetzt');
               notify('Freigabe wurde per Alexa aktiviert');
               return;
           }
        
           if (includesAlias(spoken, DISABLE_ALIASES)) {
               setCleaningEnabled(false);
               setStatus('Freigabe per Alexa deaktiviert');
               setDecision('Freigabe per Alexa deaktiviert');
               notify('Freigabe wurde per Alexa deaktiviert');
               return;
           }
        
           if (includesAlias(spoken, CANCEL_ALIASES)) {
               resetSelection('Abbruch per Sprache');
               notify('Auswahl abgebrochen');
               return;
           }
        
           if (includesAlias(spoken, ALL_ALIASES)) {
               startAllCleaning('Alexa');
               return;
           }
        
           let matchedRoom = null;
        
           for (const room of ROOMS) {
               if (includesAlias(spoken, room.aliases)) {
                   matchedRoom = room;
                   break;
               }
           }
        
           if (!matchedRoom) {
               setDecision(`Luna-Befehl ohne bekannten Raum: ${raw}`);
               return;
           }
        
           startRoomCleaning(matchedRoom, 'Alexa');
        });
        
        /*************************************************
        * LADESTATION
        *************************************************/
        
        on({ id: DEEBOT_CHARGE, change: 'ne' }, obj => {
           if (!obj || !obj.state) return;
        
           const val = String(obj.state.val || '').toLowerCase();
        
           if (val !== 'charging') {
               chargingNotified = false;
               return;
           }
        
           if (chargingNotified) return;
        
           chargingNotified = true;
        
           clearTimer(resetTimer);
        
           resetTimer = setTimeout(() => {
               resetSelection('Luna lädt');
               logAction('steht auf der Ladestation. Auswahl wurde zurückgesetzt.');
               resetTimer = null;
           }, RESET_AFTER_CHARGING_MS);
        });
        
        /*************************************************
        * LISTENER
        *************************************************/
        
        function updateVisPosition(value) {
           const room = getRoomByLetter(value);
           const position = room ? room.name : 'Unbekannt';
           setState(STATE_POSITION_TEXT, position, true);
        }
        
        on({ id: DEEBOT_POSITION, change: 'any' }, obj => {
           updateVisPosition(obj.state.val);
        });
        
        on({ id: DEEBOT_POS_STATE, change: 'any' }, obj => {
           updateBotOnMap(obj.state.val);
        });
        
        on({ id: DEEBOT_STATUS_STATE, change: 'any' }, obj => {
           checkResetTrailByStatus(obj.state.val);
        });
        
        for (const room of ROOMS) {
           on({ id: getMapMarkState(room.id), change: 'any' }, obj => {
               updateRoomOpacity(room.id, obj.state.val);
           });
        }
        
        on({ id: STATE_MAP_ID, change: 'ne' }, obj => {
           setDecision(`Map-ID geändert auf ${obj.state.val}`);
        
           for (const room of ROOMS) {
               const mapMark = getMapMarkState(room.id);
               updateRoomOpacity(room.id, safeExistsState(mapMark) ? getState(mapMark)?.val : false);
           }
        });
        
        on({
           id: [
               DEEBOT_AREA,
               ...ROOMS.map(room => room.markAlias)
           ],
           change: 'ne'
        }, () => {
           const spotArea = getStringState(DEEBOT_AREA);
        
           if (spotArea === lastSpotArea) {
               return;
           }
        
           lastSpotArea = spotArea;
           resetRoomStates();
        
           const roomNames = getRoomNamesBySpotArea(spotArea);
        
           if (spotArea === '0,1,2,3') {
               for (const room of ROOMS) {
                   setState(room.roomState, true, true);
               }
        
               saveCleanedRooms(roomNames);
               setStatus(`saugt das Obergeschoss: ${roomListText(roomNames)}`);
               return;
           }
        
           const room = getRoomById(spotArea);
        
           if (room) {
               setState(room.roomState, true, true);
               saveCleanedRooms([room.name]);
               setStatus(`saugt ${room.name}`);
           }
        });
        
        /*************************************************
        * INITIALISIERUNG
        *************************************************/
        
        createUserStates();
        
        setTimeout(() => {
           setStatus(`Luna Skript gestartet | Version ${VERSION}`);
           setDecision(`Initialisierung abgeschlossen | Version ${VERSION}`);
        
           const currentPosition = getState(DEEBOT_POSITION);
           if (currentPosition) {
               updateVisPosition(currentPosition.val);
           }
        
           const currentBotPosition = getState(DEEBOT_POS_STATE);
           if (currentBotPosition && currentBotPosition.val) {
               updateBotOnMap(currentBotPosition.val);
           }
        
           const currentDeviceStatus = getState(DEEBOT_STATUS_STATE);
           if (currentDeviceStatus && currentDeviceStatus.val) {
               checkResetTrailByStatus(currentDeviceStatus.val);
           }
        
           for (const room of ROOMS) {
               const mapMark = getMapMarkState(room.id);
               updateRoomOpacity(room.id, safeExistsState(mapMark) ? getState(mapMark)?.val : false);
           }
        }, 2000);
        
        /*************************************************
        * ZEITSTEUERUNG
        *************************************************/
        
        schedule(CRON_ENABLE, () => {
           setCleaningEnabled(true);
           setStatus('Freigabe für nächsten Luna Reinigungstag gesetzt');
           setDecision('19:17-Freigabe aktiviert');
           notify('tägliche Freigabe wurde aktiviert. Luna darf beim nächsten Automatiklauf saugen.');
        });
        
        schedule(CRON_CHECK, () => {
           runAutomaticCleaning();
        });
        
        schedule(CRON_RESET_DAILY, () => {
           setState(STATE_LAST_HOLIDAY_NOTIFY, '', true);
           setDecision('Täglicher Luna Reset abgeschlossen');
        });
        
        /*************************************************
        * VIS / MANUELLE BEDIENUNG
        *************************************************/
        
        on({ id: STATE_MANUAL_START, change: 'any' }, obj => {
           if (obj && obj.state && obj.state.val === true) {
               setDecision('Manueller Start per VIS ausgelöst');
               runManualCleaning();
               setState(STATE_MANUAL_START, false, true);
           }
        });
        
        on({ id: STATE_MANUAL_RESET, change: 'any' }, obj => {
           if (obj && obj.state && obj.state.val === true) {
               setCleaningEnabled(false);
               resetSelection('manueller VIS Reset');
               setStatus('Luna Freigabe manuell zurückgesetzt');
               setDecision('Manueller Reset per VIS');
               notify('manueller Reset ausgeführt');
               setState(STATE_MANUAL_RESET, false, true);
           }
        });
        

        Intel NUC mit Iobroker

        sigi234S 1 Antwort Letzte Antwort
        0
        • H helfi9999

          Ecovacs Deebot interaktive Karte:


          4076a06c-81d3-4430-89c6-6c3852c56c25-image.jpeg

          Habe eine Skript entwickelt das die Karte einsetzt und die Fahrt des Saugers mit aufzeichnet. Man kann auch über die Karte die Räume auswählen.

          Für das Widget der Karte muss man folgenden Wert eintragen:

          <!-- LUNA VIS HTML v1.1 --> <div style="position:relative;width:540px;height:231px;overflow:hidden;background:none;border:none;">    <img     src="http://192.168.178.59:8082/files/ecovacs-deebot.2/lastestCleaningMapImage.png"     style="position:absolute;left:0;top:0;width:540px;height:231px;z-index:1;"   >    <svg viewBox="0 0 540 231"        style="position:absolute;left:0;top:0;width:540px;height:231px;z-index:2;pointer-events:auto;">      <polyline       points="{0_userdata.0.Luna.Map.trailPoints}"       fill="none"       stroke="#ff3b30"       stroke-width="2"       opacity="0.8"       style="pointer-events:none;"     />      <polygon fill="orange"       fill-opacity="{0_userdata.0.Luna.Map.room0Opacity}"       style="cursor:pointer;"       onclick="         const mapId = vis.states['0_userdata.0.Luna.Map.mapId.val'] || '1245233875';         const id = 'ecovacs-deebot.2.map.' + mapId + '.spotAreas.0.markForNextSpotAreaCleaning';         vis.setValue(id, !(vis.states[id + '.val']));       "       points="0,231 2.5,196.9 7.4,192.1 2.5,187.2 2.5,119.2 4.9,116.7 2.5,68.1 7.4,60.8 7.4,4.9 22.2,4.9 24.7,26.8 34.6,34 44.4,34 44.4,114.3 51.8,121.6 140.7,121.6 145.5,116.7 145.5,36.5 162.9,34 170.3,24.3 177.7,21.9 180.1,36.5 187.5,43.8 187.5,97.2 180.1,104.5 180.1,116.7 185.1,121.6 185.1,153.2 180.1,153.2 170.3,162.9 155.4,170.2 165.3,179.9 177.7,170.2 177.7,175 170.3,179.9 177.7,187.2 175.2,206.6 172.8,204.2 19.7,204.2 14.8,209.1 7.4,231 0,231" />      <polygon fill="lime"       fill-opacity="{0_userdata.0.Luna.Map.room1Opacity}"       style="cursor:pointer;"       onclick="         const mapId = vis.states['0_userdata.0.Luna.Map.mapId.val'] || '1245233875';         const id = 'ecovacs-deebot.2.map.' + mapId + '.spotAreas.1.markForNextSpotAreaCleaning';         vis.setValue(id, !(vis.states[id + '.val']));       "       points="190.0,153.2 190.0,121.6 197.5,116.7 271.4,116.7 278.9,99.6 293.7,104.5 308.5,104.5 313.4,111.8 313.4,126.5 323.3,131.3 318.4,153.2 311.0,158.1 313.4,221.9 308.5,231 202.4,231 202.4,175 214.7,187.2 219.7,187.2 246.8,175 254.2,162.9 246.8,155.6 244.3,158.1 209.8,155.6 204.9,160.5 192.5,153.2 190.0,153.2" />      <polygon fill="violet"       fill-opacity="{0_userdata.0.Luna.Map.room2Opacity}"       style="cursor:pointer;"       onclick="         const mapId = vis.states['0_userdata.0.Luna.Map.mapId.val'] || '1245233875';         const id = 'ecovacs-deebot.2.map.' + mapId + '.spotAreas.2.markForNextSpotAreaCleaning';         vis.setValue(id, !(vis.states[id + '.val']));       "       points="273.8,63.2 276.3,7.3 288.7,9.7 293.7,26.8 362.9,26.8 370.3,19.5 372.8,7.3 407.3,7.3 414.7,14.6 409.7,19.5 409.7,34 417.2,41.3 429.5,41.3 439.4,55.9 540,55.9 537.5,99.6 443.8,99.6 433.9,87.5 429.5,92.4 417.2,87.5 407.3,99.6 375.2,99.6 367.8,85.1 360.4,89.9 325.8,85.1 313.4,99.6 293.7,99.6 281.3,97.2 283.8,63.2 278.9,58.3 273.8,63.2" />      <polygon fill="cyan"       fill-opacity="{0_userdata.0.Luna.Map.room3Opacity}"       style="cursor:pointer;"       onclick="         const mapId = vis.states['0_userdata.0.Luna.Map.mapId.val'] || '1245233875';         const id = 'ecovacs-deebot.2.map.' + mapId + '.spotAreas.3.markForNextSpotAreaCleaning';         vis.setValue(id, !(vis.states[id + '.val']));       "       points="323.3,155.6 328.2,133.7 362.9,133.7 370.3,119.2 375.2,116.7 464.1,116.7 469.0,121.6 476.4,116.7 540,116.7 542.5,119.2 542.5,143.5 513.4,148.3 513.4,187.2 518.3,192.1 542.5,192.1 540,201.8 488.7,201.8 483.8,196.9 466.5,196.9 461.5,201.8 439.4,201.8 432.0,209.1 434.4,221.2 427.0,226.1 422.1,226.1 422.1,218.8 414.7,211.5 404.8,218.8 404.8,226.1 390.0,226.1 382.6,221.2 382.6,189.6 375.2,182.3 335.6,182.3 333.2,155.6 323.3,155.6" />    </svg>    <div style="     position:absolute;     left:{0_userdata.0.Luna.Map.pixelXcss};     top:{0_userdata.0.Luna.Map.pixelYcss};     transform:{0_userdata.0.Luna.Map.rotateCss};     width:18px;height:18px;     z-index:9999;     pointer-events:none;   ">     <img src="https://api-app.dc-eu.ww.ecouser.net/api/pim/file/get/5ba4a2cb6c2f120001c32839"          style="width:18px;height:18px;">   </div>    <span style="display:none;">     {0_userdata.0.Luna.Map.mapId}     {0_userdata.0.Luna.Map.pixelXcss}     {0_userdata.0.Luna.Map.pixelYcss}     {0_userdata.0.Luna.Map.rotateCss}     {0_userdata.0.Luna.Map.trailPoints}     {0_userdata.0.Luna.Map.room0Opacity}     {0_userdata.0.Luna.Map.room1Opacity}     {0_userdata.0.Luna.Map.room2Opacity}     {0_userdata.0.Luna.Map.room3Opacity}   </span>  </div>
          

          Demensprechend muss man seine Daten ersetzen sowie beim Hauptskript auch:

          /*************************************************
          * SAUGROBOTER-STEUERUNG LUNA
          * Version: 1.2.1
          * Automatik + Alexa + VIS + Telegram + Karte/Trail
          * Ohne zusätzliche ioBroker-Logs
          *************************************************/
          
          const VERSION = '1.2.1';
          
          const ROBOT_NAME = 'Luna';
          
          const TELEGRAM_INSTANCE = 'telegram.0';
          const TELEGRAM_USER_ID  = '1365513925';
          const TELEGRAM_REQUEST  = `${TELEGRAM_INSTANCE}.communicate.request`;
          
          const STATE_KERSTIN = 'fb-checkpresence.0.Kerstin-Handy.presence';
          const STATE_THOMAS  = 'fb-checkpresence.0.Thomas-IPhone.presence';
          const STATE_FEIERTAG = 'feiertage.0.heute.boolean';
          
          const STATE_BASE                = '0_userdata.0.Luna';
          const STATE_SAUGEN_FREIGABE     = `${STATE_BASE}.saugen`;
          const STATE_STATUS_TEXT         = `${STATE_BASE}.statusText`;
          const STATE_LAST_RUN            = `${STATE_BASE}.lastRun`;
          const STATE_LAST_DECISION       = `${STATE_BASE}.lastDecision`;
          const STATE_MANUAL_START        = `${STATE_BASE}.manualStart`;
          const STATE_MANUAL_RESET        = `${STATE_BASE}.manualReset`;
          const STATE_POSITION_TEXT       = `${STATE_BASE}.Position`;
          const STATE_WAS_SAUGEN          = `${STATE_BASE}.Was_saugen`;
          const STATE_LAST_HOLIDAY_NOTIFY = `${STATE_BASE}.lastHolidayNotification`;
          const STATE_LAST_CLEANED_ROOMS  = `${STATE_BASE}.lastCleanedRooms`;
          
          const ALEXA_STATE = 'alexa2.0.History.summary';
          const ROBOT_LUNA  = 'luna';
          const ROBOT_SKY   = 'sky';
          
          const DEEBOT_PREFIX   = 'ecovacs-deebot.2';
          const DEEBOT_AREA     = `${DEEBOT_PREFIX}.control.spotArea`;
          const DEEBOT_CHARGE   = `${DEEBOT_PREFIX}.info.chargestatus`;
          const DEEBOT_POSITION = `${DEEBOT_PREFIX}.map.deebotPositionCurrentSpotAreaName`;
          
          const DEFAULT_MAP_ID = '1245233875';
          
          const DEEBOT_POS_STATE    = `${DEEBOT_PREFIX}.map.deebotPosition`;
          const DEEBOT_STATUS_STATE = `${DEEBOT_PREFIX}.info.deviceStatus`;
          
          const MAP_BASE      = `${STATE_BASE}.Map`;
          const STATE_MAP_ID  = `${MAP_BASE}.mapId`;
          const STATE_PIXEL_X = `${MAP_BASE}.pixelXcss`;
          const STATE_PIXEL_Y = `${MAP_BASE}.pixelYcss`;
          const STATE_ROTATE  = `${MAP_BASE}.rotateCss`;
          const STATE_TRAIL   = `${MAP_BASE}.trailPoints`;
          
          const minX = -250;
          const maxX = 10700;
          const minY = -4350;
          const maxY = 400;
          
          const imgW = 540;
          const imgH = 231;
          
          const CRON_ENABLE      = '17 19 * * *';
          const CRON_CHECK       = '*/5 9-12 * * 1-5';
          const CRON_RESET_DAILY = '0 0 * * *';
          
          const RESET_AFTER_CHARGING_MS = 10000;
          const DEBOUNCE_MS             = 10000;
          
          const SETTINGS = {
             SEND_TELEGRAM: true,
             SKIP_ON_HOLIDAY: true
          };
          
          const DAYS = {
             MONDAY: 1,
             WEDNESDAY: 3,
             FRIDAY: 5
          };
          
          const ROOMS = [
             {
                 id: 0,
                 name: 'Schlafzimmer',
                 mapLetter: 'A',
                 roomState: `${STATE_BASE}.Schlafzimmer`,
                 spotAlias: 'alias.0.Luna.Spot0.ACTUAL',
                 markAlias: 'alias.0.Luna.Mark0.ACTUAL',
                 aliases: ['schlafzimmer']
             },
             {
                 id: 1,
                 name: 'Flur',
                 mapLetter: 'B',
                 roomState: `${STATE_BASE}.Flur`,
                 spotAlias: 'alias.0.Luna.Spot1.ACTUAL',
                 markAlias: 'alias.0.Luna.Mark1.ACTUAL',
                 aliases: ['flur']
             },
             {
                 id: 2,
                 name: 'Kinderzimmer',
                 mapLetter: 'C',
                 roomState: `${STATE_BASE}.Kinderzimmer`,
                 spotAlias: 'alias.0.Luna.Spot2.ACTUAL',
                 markAlias: 'alias.0.Luna.Mark2.ACTUAL',
                 aliases: ['kinderzimmer']
             },
             {
                 id: 3,
                 name: 'Badezimmer',
                 mapLetter: 'D',
                 roomState: `${STATE_BASE}.Badezimmer`,
                 spotAlias: 'alias.0.Luna.Spot3.ACTUAL',
                 markAlias: 'alias.0.Luna.Mark3.ACTUAL',
                 aliases: ['badezimmer', 'bad']
             }
          ];
          
          const ALL_ALIASES = ['obergeschoss', 'oben', 'alles'];
          
          const CANCEL_ALIASES = [
             'stopp',
             'stop',
             'abbrechen',
             'abbruch',
             'nichts',
             'doch nicht'
          ];
          
          const ENABLE_ALIASES = [
             'freigeben',
             'freigabe',
             'darf saugen',
             'saugen erlauben',
             'automatik an',
             'automatik aktivieren'
          ];
          
          const DISABLE_ALIASES = [
             'nicht saugen',
             'freigabe aus',
             'freigabe deaktivieren',
             'saugen verbieten',
             'automatik aus',
             'automatik deaktivieren'
          ];
          
          const AUTOMATIC_ROOM_ID = 0;
          const AUTOMATIC_DAYS = [DAYS.MONDAY, DAYS.WEDNESDAY, DAYS.FRIDAY];
          
          let lastActionText = '';
          let lastActionTime = 0;
          let lastSpotArea = '';
          let resetTimer = null;
          
          let trail = [];
          let wasCleaning = false;
          let chargingNotified = false;
          
          function notify(text) {
             if (!SETTINGS.SEND_TELEGRAM) return;
          
             sendTo(TELEGRAM_INSTANCE, 'send', {
                 chatId: TELEGRAM_USER_ID,
                 text: `${ROBOT_NAME}: ${text}`
             });
          }
          
          function safeExistsState(id) {
             try {
                 return existsState(id);
             } catch (e) {
                 return false;
             }
          }
          
          function getBoolState(id, fallback = false) {
             try {
                 if (!safeExistsState(id)) return fallback;
                 const s = getState(id);
                 if (!s || s.val === null || s.val === undefined) return fallback;
                 return !!s.val;
             } catch (e) {
                 return fallback;
             }
          }
          
          function getStringState(id, fallback = '') {
             try {
                 if (!safeExistsState(id)) return fallback;
                 const s = getState(id);
                 if (!s || s.val === null || s.val === undefined) return fallback;
                 return String(s.val);
             } catch (e) {
                 return fallback;
             }
          }
          
          function normalize(text) {
             return String(text || '')
                 .toLowerCase()
                 .normalize('NFD').replace(/[\u0300-\u036f]/g, '')
                 .replace(/[^\w\s]/g, ' ')
                 .replace(/\s+/g, ' ')
                 .trim();
          }
          
          function includesAlias(text, aliases) {
             return aliases.some(alias => text.includes(normalize(alias)));
          }
          
          function getTimestamp() {
             return formatDate(new Date(), 'YYYY-MM-DD hh:mm:ss');
          }
          
          function setStatus(text) {
             setState(STATE_STATUS_TEXT, text, true);
          }
          
          function setDecision(text) {
             setState(STATE_LAST_DECISION, text, true);
          }
          
          function logAction(text) {
             const now = Date.now();
          
             if (text === lastActionText && now - lastActionTime < DEBOUNCE_MS) {
                 return;
             }
          
             lastActionText = text;
             lastActionTime = now;
          
             setState(STATE_WAS_SAUGEN, text, true);
             setStatus(text);
             notify(text);
          }
          
          function isSomeoneHome() {
             return getBoolState(STATE_KERSTIN) || getBoolState(STATE_THOMAS);
          }
          
          function isCleaningEnabled() {
             return getBoolState(STATE_SAUGEN_FREIGABE);
          }
          
          function setCleaningEnabled(value) {
             setState(STATE_SAUGEN_FREIGABE, value, true);
          }
          
          function isHoliday() {
             return getBoolState(STATE_FEIERTAG, false);
          }
          
          function getWeekday() {
             return new Date().getDay();
          }
          
          function isSameDay(ts1, ts2) {
             if (!ts1 || !ts2) return false;
             return new Date(ts1).toDateString() === new Date(ts2).toDateString();
          }
          
          function sendHolidayNotificationOncePerDay(message) {
             const now = new Date();
             const lastNotify = getStringState(STATE_LAST_HOLIDAY_NOTIFY, '');
          
             if (!isSameDay(now, lastNotify)) {
                 notify(message);
                 setState(STATE_LAST_HOLIDAY_NOTIFY, now.toISOString(), true);
             }
          }
          
          function getRoomById(id) {
             return ROOMS.find(room => String(room.id) === String(id));
          }
          
          function getRoomByLetter(letter) {
             return ROOMS.find(room => room.mapLetter === String(letter));
          }
          
          function getRoomNamesBySpotArea(spotArea) {
             if (spotArea === '0,1,2,3') {
                 return ROOMS.map(room => room.name);
             }
          
             const room = getRoomById(spotArea);
             return room ? [room.name] : [];
          }
          
          function roomListText(roomNames) {
             if (!roomNames || roomNames.length === 0) return 'keine Räume';
             return roomNames.join(', ');
          }
          
          function clearTimer(timer) {
             if (timer) clearTimeout(timer);
          }
          
          function getCurrentMapId() {
             return getStringState(STATE_MAP_ID, DEFAULT_MAP_ID);
          }
          
          function getMapMarkState(roomId) {
             return `${DEEBOT_PREFIX}.map.${getCurrentMapId()}.spotAreas.${roomId}.markForNextSpotAreaCleaning`;
          }
          
          function clearAllMarks() {
             for (const room of ROOMS) {
                 setState(room.markAlias, false);
             }
          
             for (const room of ROOMS) {
                 const mapMark = getMapMarkState(room.id);
                 if (safeExistsState(mapMark)) {
                     setState(mapMark, false);
                 }
             }
          }
          
          function resetRoomStates() {
             for (const room of ROOMS) {
                 setState(room.roomState, false, true);
             }
          }
          
          function resetSelection(reason) {
             clearAllMarks();
             resetRoomStates();
             setState(DEEBOT_AREA, ' ', true);
             lastSpotArea = '';
             setDecision(`Auswahl zurückgesetzt: ${reason}`);
          }
          
          function saveCleanedRooms(roomNames) {
             const text = roomListText(roomNames);
             setState(STATE_LAST_CLEANED_ROOMS, text, true);
             setState(STATE_WAS_SAUGEN, text, true);
          }
          
          function startRoomCleaning(room, source) {
             if (!room) return;
          
             chargingNotified = false;
          
             setState(room.spotAlias, true);
          
             const cleanedRooms = [room.name];
             saveCleanedRooms(cleanedRooms);
          
             setState(STATE_LAST_RUN, getTimestamp(), true);
             setDecision(`${source}: ${room.name} gestartet`);
             logAction(`${source}: ${room.name} wird gesaugt`);
          }
          
          function startAllCleaning(source) {
             chargingNotified = false;
          
             setState(DEEBOT_AREA, '0,1,2,3');
          
             const cleanedRooms = ROOMS.map(room => room.name);
             saveCleanedRooms(cleanedRooms);
          
             setState(STATE_LAST_RUN, getTimestamp(), true);
             setDecision(`${source}: Obergeschoss gestartet`);
             logAction(`${source}: Obergeschoss wird gesaugt: ${roomListText(cleanedRooms)}`);
          }
          
          /*************************************************
          * TELEGRAM STATUS
          *************************************************/
          
          function getCleaningStateText() {
             const deviceStatus = getStringState(DEEBOT_STATUS_STATE, '').toLowerCase();
             const chargeStatus = getStringState(DEEBOT_CHARGE, '').toLowerCase();
          
             const isCleaning =
                 deviceStatus.includes('clean') ||
                 deviceStatus.includes('spot') ||
                 deviceStatus.includes('auto') ||
                 deviceStatus.includes('edge');
          
             if (isCleaning) return 'Ja';
             if (chargeStatus === 'charging') return 'Nein, Luna steht auf der Ladestation';
          
             return 'Unbekannt';
          }
          
          function sendLunaStatus() {
             const saugtWas = getStringState(STATE_WAS_SAUGEN, 'nichts bekannt');
             const lastCleaned = getStringState(STATE_LAST_CLEANED_ROOMS, 'nichts bekannt');
             const freigabe = isCleaningEnabled() ? 'Ja' : 'Nein';
             const position = getStringState(STATE_POSITION_TEXT, 'Unbekannt');
             const statusText = getStringState(STATE_STATUS_TEXT, '-');
             const letzteEntscheidung = getStringState(STATE_LAST_DECISION, '-');
             const lastRun = getStringState(STATE_LAST_RUN, '-');
             const mapId = getCurrentMapId();
             const saugtGerade = getCleaningStateText();
          
             notify(
                 `Status:\n` +
                 `Saugt gerade: ${saugtGerade}\n` +
                 `Was: ${saugtWas}\n` +
                 `Zuletzt gereinigte Räume: ${lastCleaned}\n` +
                 `Freigabe nächster Automatiklauf: ${freigabe}\n` +
                 `Position: ${position}\n` +
                 `Letzter Lauf: ${lastRun}\n` +
                 `Status VIS: ${statusText}\n` +
                 `Letzte Entscheidung: ${letzteEntscheidung}\n` +
                 `Map-ID: ${mapId}`
             );
          }
          
          on({ id: TELEGRAM_REQUEST, change: 'any' }, obj => {
             if (!obj || !obj.state || !obj.state.val) return;
          
             let raw = String(obj.state.val);
          
             raw = raw.replace(/^\[[^\]]+\]/, '').trim();
          
             const text = normalize(raw);
          
             const isLunaStatusRequest =
                 text.includes('was macht luna') ||
                 text.includes('luna status') ||
                 text.includes('status luna') ||
                 text.includes('luna freigabe') ||
                 text.includes('luna saugt') ||
                 text.includes('luna info') ||
                 text === 'luna';
          
             if (!isLunaStatusRequest) {
                 return;
             }
          
             sendLunaStatus();
          });
          
          /*************************************************
          * VIS KARTE / POSITION / TRAIL
          *************************************************/
          
          function toPixel(x, y) {
             return {
                 x: ((x - minX) / (maxX - minX)) * imgW,
                 y: imgH - ((y - minY) / (maxY - minY)) * imgH
             };
          }
          
          function parseBotPosition(value) {
             if (!value) return null;
          
             const p = String(value).split(',').map(Number);
             if (p.length < 3 || p.some(isNaN)) return null;
          
             return {
                 x: p[0],
                 y: p[1],
                 angle: p[2]
             };
          }
          
          function updateBotOnMap(value) {
             const pos = parseBotPosition(value);
             if (!pos) return;
          
             const pixel = toPixel(pos.x, pos.y);
          
             setState(STATE_PIXEL_X, pixel.x.toFixed(1) + 'px', true);
             setState(STATE_PIXEL_Y, pixel.y.toFixed(1) + 'px', true);
             setState(STATE_ROTATE, `translate(-50%,-50%) rotate(${pos.angle}deg)`, true);
          
             const last = trail[trail.length - 1];
          
             if (!last || Math.abs(last.x - pixel.x) > 1 || Math.abs(last.y - pixel.y) > 1) {
                 trail.push(pixel);
          
                 if (trail.length > 500) {
                     trail.shift();
                 }
          
                 const points = trail
                     .map(pt => `${pt.x.toFixed(1)},${pt.y.toFixed(1)}`)
                     .join(' ');
          
                 setState(STATE_TRAIL, points, true);
             }
          }
          
          function resetTrail() {
             trail = [];
             setState(STATE_TRAIL, '', true);
          }
          
          function checkResetTrailByStatus(status) {
             if (!status) return;
          
             const s = String(status).toLowerCase();
          
             const isCleaning =
                 s.includes('clean') ||
                 s.includes('spot') ||
                 s.includes('auto') ||
                 s.includes('edge');
          
             if (isCleaning && !wasCleaning) {
                 resetTrail();
                 wasCleaning = true;
                 chargingNotified = false;
             }
          
             const isFinished =
                 s.includes('charging') ||
                 s.includes('charge') ||
                 s.includes('dock') ||
                 s.includes('idle') ||
                 s.includes('stop') ||
                 s.includes('finish') ||
                 s.includes('complete') ||
                 s.includes('standby');
          
             if (isFinished) {
                 wasCleaning = false;
             }
          }
          
          function updateRoomOpacity(roomNumber, value) {
             const selected = value === true || value === 'true' || value === 1 || value === '1';
             const opacity = selected ? 0.75 : 0.25;
          
             setState(`${MAP_BASE}.room${roomNumber}Opacity`, opacity, true);
          }
          
          /*************************************************
          * STATES AUTOMATISCH ANLEGEN
          *************************************************/
          
          function createUserStates() {
             createState(STATE_SAUGEN_FREIGABE, false, {
                 name: 'Saugfreigabe für nächsten Reinigungslauf',
                 type: 'boolean',
                 role: 'switch.enable',
                 read: true,
                 write: true,
                 def: false
             });
          
             createState(STATE_STATUS_TEXT, '', {
                 name: 'Statusanzeige Luna',
                 type: 'string',
                 role: 'text',
                 read: true,
                 write: false,
                 def: ''
             });
          
             createState(STATE_LAST_RUN, '', {
                 name: 'Letzter Luna Reinigungslauf',
                 type: 'string',
                 role: 'text',
                 read: true,
                 write: false,
                 def: ''
             });
          
             createState(STATE_LAST_DECISION, '', {
                 name: 'Letzte Luna Entscheidung',
                 type: 'string',
                 role: 'text',
                 read: true,
                 write: false,
                 def: ''
             });
          
             createState(STATE_MANUAL_START, false, {
                 name: 'Manueller Luna Start per VIS',
                 type: 'boolean',
                 role: 'button',
                 read: true,
                 write: true,
                 def: false
             });
          
             createState(STATE_MANUAL_RESET, false, {
                 name: 'Manueller Luna Reset',
                 type: 'boolean',
                 role: 'button',
                 read: true,
                 write: true,
                 def: false
             });
          
             createState(STATE_POSITION_TEXT, '', {
                 name: 'Aktuelle Luna Position',
                 type: 'string',
                 role: 'text',
                 read: true,
                 write: false,
                 def: ''
             });
          
             createState(STATE_WAS_SAUGEN, '', {
                 name: 'Was saugt Luna',
                 type: 'string',
                 role: 'text',
                 read: true,
                 write: false,
                 def: ''
             });
          
             createState(STATE_LAST_HOLIDAY_NOTIFY, '', {
                 name: 'Letzte Feiertags-Benachrichtigung Luna',
                 type: 'string',
                 role: 'text',
                 read: true,
                 write: false,
                 def: ''
             });
          
             createState(STATE_LAST_CLEANED_ROOMS, '', {
                 name: 'Zuletzt gesaugte Räume Luna',
                 type: 'string',
                 role: 'text',
                 read: true,
                 write: false,
                 def: ''
             });
          
             for (const room of ROOMS) {
                 createState(room.roomState, false, {
                     name: `Luna Raum aktiv: ${room.name}`,
                     type: 'boolean',
                     role: 'indicator',
                     read: true,
                     write: false,
                     def: false
                 });
             }
          
             createState(STATE_MAP_ID, DEFAULT_MAP_ID, {
                 name: 'Luna aktuelle Map ID',
                 type: 'string',
                 role: 'text',
                 read: true,
                 write: true,
                 def: DEFAULT_MAP_ID
             });
          
             createState(STATE_PIXEL_X, '0px', {
                 name: 'Luna Karte X Position CSS',
                 type: 'string',
                 role: 'text',
                 read: true,
                 write: true,
                 def: '0px'
             });
          
             createState(STATE_PIXEL_Y, '0px', {
                 name: 'Luna Karte Y Position CSS',
                 type: 'string',
                 role: 'text',
                 read: true,
                 write: true,
                 def: '0px'
             });
          
             createState(STATE_ROTATE, 'translate(-50%,-50%) rotate(0deg)', {
                 name: 'Luna Karte Rotation CSS',
                 type: 'string',
                 role: 'text',
                 read: true,
                 write: true,
                 def: 'translate(-50%,-50%) rotate(0deg)'
             });
          
             createState(STATE_TRAIL, '', {
                 name: 'Luna Karte Fahrspur Punkte',
                 type: 'string',
                 role: 'text',
                 read: true,
                 write: true,
                 def: ''
             });
          
             for (const room of ROOMS) {
                 createState(`${MAP_BASE}.room${room.id}Opacity`, 0.25, {
                     name: `Luna Karte Raum ${room.id} Deckkraft`,
                     type: 'number',
                     role: 'value',
                     read: true,
                     write: true,
                     def: 0.25
                 });
             }
          }
          
          /*************************************************
          * AUTOMATIK
          *************************************************/
          
          function shouldCleanNow() {
             if (!isCleaningEnabled()) {
                 setDecision('Abbruch: Freigabe ist false');
                 return false;
             }
          
             if (!AUTOMATIC_DAYS.includes(getWeekday())) {
                 setDecision('Heute ist kein Luna Reinigungstag');
                 return false;
             }
          
             if (SETTINGS.SKIP_ON_HOLIDAY && isHoliday()) {
                 const room = getRoomById(AUTOMATIC_ROOM_ID);
                 const roomsText = room ? room.name : 'Automatikraum';
          
                 const msg = `Heute keine Reinigung wegen Feiertag. Geplant gewesen wäre: ${roomsText}.`;
          
                 setStatus(msg);
                 setDecision('Abbruch: Feiertag');
                 sendHolidayNotificationOncePerDay(msg);
                 return false;
             }
          
             if (isSomeoneHome()) {
                 setDecision('Abbruch: jemand ist zuhause');
                 return false;
             }
          
             return true;
          }
          
          function runAutomaticCleaning() {
             if (!shouldCleanNow()) return;
          
             const room = getRoomById(AUTOMATIC_ROOM_ID);
             if (!room) {
                 setDecision('Abbruch: Automatikraum nicht gefunden');
                 return;
             }
          
             startRoomCleaning(room, 'Automatik');
             setCleaningEnabled(false);
          }
          
          function runManualCleaning() {
             const room = getRoomById(AUTOMATIC_ROOM_ID);
             startRoomCleaning(room, 'Manueller Start');
          }
          
          /*************************************************
          * ALEXA
          *************************************************/
          
          on({ id: ALEXA_STATE, change: 'any' }, obj => {
             const raw = obj.state.val;
             const spoken = normalize(raw);
          
             if (!spoken) return;
          
             if (spoken.includes(ROBOT_SKY)) {
                 setDecision(`Sky-Befehl ignoriert: ${raw}`);
                 return;
             }
          
             if (!spoken.includes(ROBOT_LUNA)) {
                 return;
             }
          
             if (includesAlias(spoken, ENABLE_ALIASES)) {
                 setCleaningEnabled(true);
                 setStatus('Freigabe per Alexa aktiviert');
                 setDecision('Freigabe per Alexa gesetzt');
                 notify('Freigabe wurde per Alexa aktiviert');
                 return;
             }
          
             if (includesAlias(spoken, DISABLE_ALIASES)) {
                 setCleaningEnabled(false);
                 setStatus('Freigabe per Alexa deaktiviert');
                 setDecision('Freigabe per Alexa deaktiviert');
                 notify('Freigabe wurde per Alexa deaktiviert');
                 return;
             }
          
             if (includesAlias(spoken, CANCEL_ALIASES)) {
                 resetSelection('Abbruch per Sprache');
                 notify('Auswahl abgebrochen');
                 return;
             }
          
             if (includesAlias(spoken, ALL_ALIASES)) {
                 startAllCleaning('Alexa');
                 return;
             }
          
             let matchedRoom = null;
          
             for (const room of ROOMS) {
                 if (includesAlias(spoken, room.aliases)) {
                     matchedRoom = room;
                     break;
                 }
             }
          
             if (!matchedRoom) {
                 setDecision(`Luna-Befehl ohne bekannten Raum: ${raw}`);
                 return;
             }
          
             startRoomCleaning(matchedRoom, 'Alexa');
          });
          
          /*************************************************
          * LADESTATION
          *************************************************/
          
          on({ id: DEEBOT_CHARGE, change: 'ne' }, obj => {
             if (!obj || !obj.state) return;
          
             const val = String(obj.state.val || '').toLowerCase();
          
             if (val !== 'charging') {
                 chargingNotified = false;
                 return;
             }
          
             if (chargingNotified) return;
          
             chargingNotified = true;
          
             clearTimer(resetTimer);
          
             resetTimer = setTimeout(() => {
                 resetSelection('Luna lädt');
                 logAction('steht auf der Ladestation. Auswahl wurde zurückgesetzt.');
                 resetTimer = null;
             }, RESET_AFTER_CHARGING_MS);
          });
          
          /*************************************************
          * LISTENER
          *************************************************/
          
          function updateVisPosition(value) {
             const room = getRoomByLetter(value);
             const position = room ? room.name : 'Unbekannt';
             setState(STATE_POSITION_TEXT, position, true);
          }
          
          on({ id: DEEBOT_POSITION, change: 'any' }, obj => {
             updateVisPosition(obj.state.val);
          });
          
          on({ id: DEEBOT_POS_STATE, change: 'any' }, obj => {
             updateBotOnMap(obj.state.val);
          });
          
          on({ id: DEEBOT_STATUS_STATE, change: 'any' }, obj => {
             checkResetTrailByStatus(obj.state.val);
          });
          
          for (const room of ROOMS) {
             on({ id: getMapMarkState(room.id), change: 'any' }, obj => {
                 updateRoomOpacity(room.id, obj.state.val);
             });
          }
          
          on({ id: STATE_MAP_ID, change: 'ne' }, obj => {
             setDecision(`Map-ID geändert auf ${obj.state.val}`);
          
             for (const room of ROOMS) {
                 const mapMark = getMapMarkState(room.id);
                 updateRoomOpacity(room.id, safeExistsState(mapMark) ? getState(mapMark)?.val : false);
             }
          });
          
          on({
             id: [
                 DEEBOT_AREA,
                 ...ROOMS.map(room => room.markAlias)
             ],
             change: 'ne'
          }, () => {
             const spotArea = getStringState(DEEBOT_AREA);
          
             if (spotArea === lastSpotArea) {
                 return;
             }
          
             lastSpotArea = spotArea;
             resetRoomStates();
          
             const roomNames = getRoomNamesBySpotArea(spotArea);
          
             if (spotArea === '0,1,2,3') {
                 for (const room of ROOMS) {
                     setState(room.roomState, true, true);
                 }
          
                 saveCleanedRooms(roomNames);
                 setStatus(`saugt das Obergeschoss: ${roomListText(roomNames)}`);
                 return;
             }
          
             const room = getRoomById(spotArea);
          
             if (room) {
                 setState(room.roomState, true, true);
                 saveCleanedRooms([room.name]);
                 setStatus(`saugt ${room.name}`);
             }
          });
          
          /*************************************************
          * INITIALISIERUNG
          *************************************************/
          
          createUserStates();
          
          setTimeout(() => {
             setStatus(`Luna Skript gestartet | Version ${VERSION}`);
             setDecision(`Initialisierung abgeschlossen | Version ${VERSION}`);
          
             const currentPosition = getState(DEEBOT_POSITION);
             if (currentPosition) {
                 updateVisPosition(currentPosition.val);
             }
          
             const currentBotPosition = getState(DEEBOT_POS_STATE);
             if (currentBotPosition && currentBotPosition.val) {
                 updateBotOnMap(currentBotPosition.val);
             }
          
             const currentDeviceStatus = getState(DEEBOT_STATUS_STATE);
             if (currentDeviceStatus && currentDeviceStatus.val) {
                 checkResetTrailByStatus(currentDeviceStatus.val);
             }
          
             for (const room of ROOMS) {
                 const mapMark = getMapMarkState(room.id);
                 updateRoomOpacity(room.id, safeExistsState(mapMark) ? getState(mapMark)?.val : false);
             }
          }, 2000);
          
          /*************************************************
          * ZEITSTEUERUNG
          *************************************************/
          
          schedule(CRON_ENABLE, () => {
             setCleaningEnabled(true);
             setStatus('Freigabe für nächsten Luna Reinigungstag gesetzt');
             setDecision('19:17-Freigabe aktiviert');
             notify('tägliche Freigabe wurde aktiviert. Luna darf beim nächsten Automatiklauf saugen.');
          });
          
          schedule(CRON_CHECK, () => {
             runAutomaticCleaning();
          });
          
          schedule(CRON_RESET_DAILY, () => {
             setState(STATE_LAST_HOLIDAY_NOTIFY, '', true);
             setDecision('Täglicher Luna Reset abgeschlossen');
          });
          
          /*************************************************
          * VIS / MANUELLE BEDIENUNG
          *************************************************/
          
          on({ id: STATE_MANUAL_START, change: 'any' }, obj => {
             if (obj && obj.state && obj.state.val === true) {
                 setDecision('Manueller Start per VIS ausgelöst');
                 runManualCleaning();
                 setState(STATE_MANUAL_START, false, true);
             }
          });
          
          on({ id: STATE_MANUAL_RESET, change: 'any' }, obj => {
             if (obj && obj.state && obj.state.val === true) {
                 setCleaningEnabled(false);
                 resetSelection('manueller VIS Reset');
                 setStatus('Luna Freigabe manuell zurückgesetzt');
                 setDecision('Manueller Reset per VIS');
                 notify('manueller Reset ausgeführt');
                 setState(STATE_MANUAL_RESET, false, true);
             }
          });
          

          sigi234S Online
          sigi234S Online
          sigi234
          Forum Testing Most Active
          schrieb am zuletzt editiert von
          #4

          @helfi9999
          So ganz begreife ich nicht wo ich was ersetzen muss, werden die DP automatisch angelegt?

          Bitte benutzt das Voting rechts unten im Beitrag wenn er euch geholfen hat.
          Immer Daten sichern!

          H 1 Antwort Letzte Antwort
          0
          • sigi234S sigi234

            @helfi9999
            So ganz begreife ich nicht wo ich was ersetzen muss, werden die DP automatisch angelegt?

            H Online
            H Online
            helfi9999
            schrieb zuletzt editiert von
            #5

            @sigi234 Hallo werde am Wochenende es umändern zum vereinfachen.

            Intel NUC mit Iobroker

            1 Antwort Letzte Antwort
            1

            Hey! Du scheinst an dieser Unterhaltung interessiert zu sein, hast aber noch kein Konto.

            Hast du es satt, bei jedem Besuch durch die gleichen Beiträge zu scrollen? Wenn du dich für ein Konto anmeldest, kommst du immer genau dorthin zurück, wo du zuvor warst, und kannst dich über neue Antworten benachrichtigen lassen (entweder per E-Mail oder Push-Benachrichtigung). Du kannst auch Lesezeichen speichern und Beiträge positiv bewerten, um anderen Community-Mitgliedern deine Wertschätzung zu zeigen.

            Mit deinem Input könnte dieser Beitrag noch besser werden 💗

            Registrieren Anmelden
            Antworten
            • In einem neuen Thema antworten
            Anmelden zum Antworten
            • Älteste zuerst
            • Neuste zuerst
            • Meiste Stimmen


            Support us

            ioBroker
            Community Adapters
            Donate

            312

            Online

            33.0k

            Benutzer

            83.7k

            Themen

            1.3m

            Beiträge
            Community
            Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen | Einwilligungseinstellungen
            ioBroker Community 2014-2026
            logo
            • Anmelden

            • Du hast noch kein Konto? Registrieren

            • Anmelden oder registrieren, um zu suchen
            • Erster Beitrag
              Letzter Beitrag
            0
            • Home
            • Aktuell
            • Tags
            • Ungelesen 0
            • Kategorien
            • Unreplied
            • Beliebt
            • GitHub
            • Docu
            • Hilfe