Skip to content
  • Home
  • Aktuell
  • Tags
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • GitHub
  • Docu
  • Hilfe
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • 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. [Vorlage] Anwesenheitssimulation mit dauerhaftem Lernen

NEWS

  • Monatsrückblick Januar/Februar 2026 ist online!
    BluefoxB
    Bluefox
    16
    1
    347

  • Jahresrückblick 2025 – unser neuer Blogbeitrag ist online! ✨
    BluefoxB
    Bluefox
    17
    1
    4.8k

  • Neuer Blogbeitrag: Monatsrückblick - Dezember 2025 🎄
    BluefoxB
    Bluefox
    13
    1
    1.3k

[Vorlage] Anwesenheitssimulation mit dauerhaftem Lernen

Geplant Angeheftet Gesperrt Verschoben Skripten / Logik
javascripttemplatesecurity
2 Beiträge 2 Kommentatoren 78 Aufrufe 4 Watching
  • Ä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.
  • M Offline
    M Offline
    mrMuppet
    schrieb am zuletzt editiert von
    #1

    Hallo zusammen,

    ich möchte euch heute ein Projekt vorstellen, das das Thema Anwesenheitssimulation einmal anders angeht. Anstatt starrer Pläne oder durchschaubarer Zufallsschaltungen nutzt dieses Skript einen Snapshot-Ansatz, der sich vollautomatisch eurem echten Leben anpasst.

    Das Konzept: Ein System, das mitdenkt

    Das Skript erstellt alle 15 Minuten einen "Snapshot" eurer Lichter (oder anderer Geräte) – aber nur, wenn ihr wirklich zu Hause seid. Im Abwesenheitsmodus (Urlaub) spielt es diese Aufzeichnungen zeitversetzt wieder ab.

    Die Highlights:

    • Absolut wartungsfrei: Einmal eingerichtet, müsst ihr nie wieder eingreifen. Keine Sommer-/Winterzeit-Umstellung, kein Nachjustieren bei neuen Gewohnheiten.
    • Ganzjährig nutzbar: Da das Skript kontinuierlich lernt und alte Snapshots nach 5 Tagen automatisch überschreibt (Ringspeicher), "weiß" es im Dezember von selbst, dass das Licht früher brennen muss als im Juli.
    • Natürliches Verhalten: Es spiegelt euer echtes Leben. Werden die Tage kürzer oder ändert sich euer Rhythmus, lernt die Simulation das einfach mit.
    • Intelligente Variation: Aus den Snapshots der letzten Tage wird per Zufall gewählt und mit einem "Jitter" (zufällige Verzögerung) geschaltet. Das wirkt für Außenstehende absolut menschlich.

    Einrichtung & Installation

    1. Datenpunkte 0_userdata.0.Anwesenheit (Urlaubs-Trigger) und 0_userdata.0.Anwesenheitssimulation_Snapshots (JSON-Speicher) anlegen.
    2. Eure Geräte-IDs im devices-Array eintragen. Fertig.

    Das Skript (TypeScript):

    // =====================================================================
    // TITEL: Smart Presence Simulation (KI-Ghost-Mode)
    //
    // ZUSAMMENFASSUNG:
    // Dieses Skript simuliert Anwesenheit bei Abwesenheit/Urlaub durch das 
    // Erlernen echter Gewohnheiten anstatt durch starre, unnatürliche Timer.
    // 
    // 1. Lern-Modus (Snapshots): Bei Anwesenheit macht das Skript alle 15 
    //    Minuten ein "Foto" der ausgewählten Lichter. Ein zufälliger Delay 
    //    (0-5 Min) vor dem Foto verhindert das starre Lernen von genauen 
    //    Uhrzeiten (z.B. pünktlich zur Tagesschau). Es merkt sich die 
    //    letzten 5 Tage.
    // 2. Urlaubssicher: Speichert nur Daten, wenn jemand physisch zuhause 
    //    ist. Im Urlaub friert die Datenbank ein und verblasst nicht.
    // 3. KI-Chaos-Faktor (Ghost-Modus): Beim Abspielen berechnet das Skript 
    //    die mathematische Ähnlichkeit der gespeicherten Fotos. Bei starren 
    //    Routinen (z.B. Wecker morgens) schaltet es hochpräzise. Bei völlig 
    //    chaotischen Routinen (z.B. Feierabend) variiert es die Schaltzeit 
    //    um bis zu 14 Minuten.
    // =====================================================================
    
    // === 1. KONFIGURATION ===
    const DP_PRESENCE: string = '0_userdata.0.Anwesenheit'; // <-- Anwesenheits-Datenpunkt (true = zuhause, false = urlaub/weg)
    
    const LIGHTS: string[] = [
        'alias.0.Licht.Flur.STATE',        // <-- Trage hier alle Lampen ein, die 
        'alias.0.Licht.Wohnzimmer.STATE',  //     von der Straße aus sichtbar sind.
        'alias.0.Licht.Kueche.STATE',
        'alias.0.Licht.Bad.STATE'
    ];
    
    // === 2. EXPERTEN-EINSTELLUNGEN ===
    const MAX_SNAPSHOTS: number = 5;           // Wie viele Anwesenheitstage pro 15-Min-Block gemerkt werden sollen
    const MAX_SAMPLING_DELAY: number = 300000; // Max. Verzögerung beim Lernen (5 Minuten in ms) -> Verhindert "Tagesschau-Effekt"
    const MAX_PLAYBACK_DELAY: number = 840000; // Max. Verzögerung beim Abspielen (14 Minuten in ms) -> Verschleiert das 15-Min-Raster
    
    // Interner Speicher (wird vom Skript automatisch angelegt)
    const DP_STORAGE: string = '0_userdata.0.Simulation.Snapshot_DB';
    
    // ============================================================
    // === TYPEN & INTERFACES (TypeScript Spezifisch) ===
    // ============================================================
    interface LightSnapshot {
        [lightId: string]: boolean;
    }
    
    interface SnapshotDatabase {
        [slotIndex: number]: LightSnapshot[];
    }
    
    // ============================================================
    // === AB HIER NICHTS MEHR ÄNDERN ===
    // ============================================================
    
    // === HILFSFUNKTIONEN ===
    function getSlotIndex(): number {
        // Teilt den Tag in 96 Blöcke á 15 Minuten (z.B. 20:15 Uhr = Block 81)
        const now = new Date();
        return Math.floor((now.getHours() * 60 + now.getMinutes()) / 15);
    }
    
    function getSafeBoolean(oid: string, fallback: boolean = false): boolean {
        if (oid && existsState(oid)) {
            const val = getState(oid).val;
            // Strikte Konvertierung in einen echten Boolean
            return (val === true || val === 'true' || val === 1);
        }
        return fallback;
    }
    
    // ==========================================
    // 1. LERN-MODUS (Datenbank füttern)
    // ==========================================
    function runLearning(db: SnapshotDatabase, currentSlot: number): void {
        let snapshot: LightSnapshot = {};
        
        // Aktuellen Zustand aller Lampen einfrieren
        LIGHTS.forEach(light => {
            snapshot[light] = getSafeBoolean(light, false);
        });
    
        if (!db[currentSlot]) db[currentSlot] = [];
        
        // Neues Foto abheften und älteste wegwerfen, wenn das Limit erreicht ist
        db[currentSlot].push(snapshot);
        if (db[currentSlot].length > MAX_SNAPSHOTS) {
            db[currentSlot].shift();
        }
    
        // In den ioBroker Datenpunkt schreiben
        setState(DP_STORAGE, JSON.stringify(db), true);
        log(`[Simulation LERNEN] Block ${currentSlot} gespeichert. (${db[currentSlot].length}/${MAX_SNAPSHOTS} Snapshots)`, 'debug');
    }
    
    // ==========================================
    // 2. GHOST-MODUS (Berechnen & Abspielen)
    // ==========================================
    function runSimulation(db: SnapshotDatabase, currentSlot: number): void {
        if (!db[currentSlot] || db[currentSlot].length === 0) {
            // Keine Daten für diese Uhrzeit (z.B. weil das Haus neu ist) -> Alles aus zur Sicherheit
            log(`[Simulation GHOST] Keine Daten für Block ${currentSlot}. Schalte Lichter aus.`, 'debug');
            LIGHTS.forEach(light => {
                if (getSafeBoolean(light, false) === true) setState(light, false, false);
            });
            return;
        }
    
        const slotSnapshots: LightSnapshot[] = db[currentSlot];
        
        // --- DIE MAGIE: CHAOS-FAKTOR BERECHNEN ---
        // Wir wandeln alle Snapshots in Text um und zählen, wie viele unterschiedliche Kombinationen es gibt
        const uniqueCount = new Set(slotSnapshots.map(s => JSON.stringify(s))).size;
        const chaosFactor = uniqueCount / slotSnapshots.length; // Ergibt einen Wert zwischen >0 und 1.0
        
        // Daraus berechnen wir die dynamische Verzögerung für exakt diese Tageszeit
        const dynamicMaxDelay = Math.floor(chaosFactor * MAX_PLAYBACK_DELAY);
        const randomDelayMs = Math.floor(Math.random() * dynamicMaxDelay);
        
        log(`[Simulation GHOST] Block ${currentSlot} | Chaos-Faktor: ${Math.round(chaosFactor*100)}% | Geplanter Delay: ${Math.round(randomDelayMs/1000)} Sekunden`, 'info');
    
        // --- ABSPIELEN MIT BERECHNETER VERZÖGERUNG ---
        setTimeout(() => {
            // Blind ein Foto aus den gespeicherten ziehen
            const randomIndex = Math.floor(Math.random() * slotSnapshots.length);
            const chosenSnapshot = slotSnapshots[randomIndex];
            
            LIGHTS.forEach(light => {
                const targetState = chosenSnapshot[light];
                const currentState = getSafeBoolean(light, false);
                
                // Schont das System und den Funkverkehr: Nur schalten, wenn nötig
                if (targetState !== currentState) {
                    setState(light, targetState, false);
                }
            });
        }, randomDelayMs);
    }
    
    // === INIT & CRONJOB ===
    createState(DP_STORAGE, JSON.stringify({}), { type: 'string', name: 'Snapshot Datenbank (JSON)', role: 'json' }, () => {
        
        // Der Cronjob feuert gnadenlos exakt alle 15 Minuten (xx:00, xx:15, xx:30, xx:45)
        schedule("*/15 * * * *", () => {
            const isPresent = getSafeBoolean(DP_PRESENCE, true);
            
            // WICHTIG: Den aktuellen Block (Slot) SOFORT festhalten, bevor das Skript eventuell wartet!
            const currentSlot = getSlotIndex(); 
            
            // Datenbank aus dem ioBroker laden
            let db: SnapshotDatabase = {};
            try {
                const raw = getState(DP_STORAGE).val as string;
                if (raw) db = JSON.parse(raw) as SnapshotDatabase;
            } catch (e) {
                log('[Simulation] Fehler beim Lesen der Datenbank, starte mit leerer DB neu.', 'warn');
            }
    
            if (isPresent) {
                // ==========================================
                // LERN-MODUS MIT SAMPLING-VARIANZ
                // ==========================================
                // Wir warten zufällig 0 bis 5 Minuten, bevor das Foto gemacht wird.
                const samplingDelayMs = Math.floor(Math.random() * MAX_SAMPLING_DELAY); 
                
                log(`[Simulation LERNEN] Block ${currentSlot} ausgelöst. Mache das Foto in ${Math.round(samplingDelayMs/1000)} Sekunden...`, 'debug');
                
                setTimeout(() => {
                    runLearning(db, currentSlot);
                }, samplingDelayMs);
    
            } else {
                // ==========================================
                // GHOST-MODUS (ABWESEND)
                // ==========================================
                runSimulation(db, currentSlot);
            }
        });
    
        log('[Simulation] KI-Snapshot-Engine gestartet. (TypeScript Edition)');
    });
    
    

    Fazit

    Für mich war wichtig, dass ich mich vor dem Urlaub nicht um die Simulation kümmern muss. Durch den lernenden Charakter ist das System das ganze Jahr über "scharf" und bildet immer den aktuellen Lebensstil ab.

    Ich freue mich auf euer Feedback!

    ioBroker auf NUC (Celeron mit Ubuntu-Server)

    Homematic, HMIP, Hue, Unifi, Plex, Nest, Roborock, Google Assistant

    1 Antwort Letzte Antwort
    0
    • David G.D Offline
      David G.D Offline
      David G.
      schrieb zuletzt editiert von David G.
      #2

      Finde den Ansatz sehr cool und praktisch.
      Werde es evtl mal testen.

      Im Moment basiert meine Simulation auf dem Sonnenuntergang mit einen willkürlichem Offset der plus oder minus sein kann und dann dem schalten der relevanten Lampen in einer willkürlichen Reihenfolge mit willkürlichem offset.
      Das bildet natürlich keine Gewohnheiten ab. Das einzig wirklich Variable ist bei mir aber aicu nur das Klo 🚽 🪠🤣.

      Ich fände es praktisch, wenn man die Lampen über eine Funktion der DPs vergeben kann.
      So mache ich es.

      Zeigt eure Lovelace-Visualisierung klick
      (Auch ideal um sich Anregungen zu holen)

      Meine Tabellen für eure Visualisierung klick

      1 Antwort Letzte Antwort
      0
      Antworten
      • In einem neuen Thema antworten
      Anmelden zum Antworten
      • Älteste zuerst
      • Neuste zuerst
      • Meiste Stimmen


      Support us

      ioBroker
      Community Adapters
      Donate

      524

      Online

      32.7k

      Benutzer

      82.5k

      Themen

      1.3m

      Beiträge
      Community
      Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen | Einwilligungseinstellungen
      ioBroker Community 2014-2025
      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