Flow zum Erkennen nicht erreichbarer Geräte

Hallo,

ich möchte zum einen die Zuverlässigkeit meines gesamten Systems sicher stellen, aber auch ein Alarmsystem basteln. Das funktioniert nur leider nicht, wenn z.B. ein Zigbee-Türsensor ausgefallen ist und ich es nicht merke. Der Batteriestatusanzeige traue ich auch nicht so ganz….

Für das Shelly Walldisplay, das sich gerne mal unbemerkt aus dem Netzwerk verabschiedet, wäre die Funktion auch recht nützlich.

Also suche ich nach einer Möglichkeit, ausgefallene Geräte gemeldet zu bekommen, vielleicht in der Art wie Shelly es kann. Hier werden Geräte die nicht mehr erreichbar sind,direkt per Push gemeldet.

Hinzu kommt, dies sollte auch (bzw. eigentlich hauptsächlich) bei batteriebetriebenen Sensoren wie Türsensoren funktionieren, die sich an sich nur bei Aktivität melden.

Nimm die App Heimdall. Ist eine Alarm-App, aber sie kann auch melden, falls ein Gerät sich nicht mehr meldet, bzw. ausgefallen ist.

Guten Morgen, ich habe es so gelöst

Gute Idee, aber wenn du ein paar mehr Geräte hast, schreibst du dir einen Wolf mit Flows.
Für Flows gibt es ja schon die App Flow Checker, die prüft ob einer kaputt ist.
Vielleicht schreibt mal einer von unseren genialen Community Entwickler eine App die Geräte prüfen kann ?
Ansonsten bin ich leider ein Depp in Sachen HomeyScript. Damit sollte es doch normal auch gehen ?!

Für defekte Flows habe ich auch den Flow Checker im Einsatz und bin super damit zufrieden.
Reicht aber ja nicht für den Ausfall einzelner Geräte, da nicht jedes Gerät in einem Flow ist.

Mit Heimdall brauche ich nicht jedes einzelne Gerät abfragen, so wie in meinem Flow?

Heimdall kann nur Bewegungsmelder und Fensterkontakte.
Ich habs mir angeschaut. Heimdall meldet sich nur, wenn sich ein Sensor 24 Stunden nicht gerührt hat. Da haben wir aber wieder ein Problem. Ich hab Fenster, die werden vielleicht einmal im Monat geöffnet und andere mehrmals am Tag. Z.B. meine Shelly BLU melden sich gar nicht, wenn sie nicht geöffnet werden. Gott sei Dank sind die so zuverlässig. Mir ist bisher noch keiner ausgefallen.
Wie gesagt, ich hab keine Ahnung ob und wie man das mit einer App oder HomeyScript lösen könnte.

1 Like

Habe mir Heimdall auch gerade angeschaut und er findet nur Bewegungsmelder und Fenstersensoren.
Ich glaube, somit bin ich mit meinem Flow erstmal auf der sicherern Seite und lasse es weiter laufen.

Alternativ kann man auch Device Capabilities nutzen. Das hab ich für ein paar Gerätetypen eingestellt, weil z.B. Netatmo und Twinkly manchmal weg sind. Dann schalte ich die per smarter Steckdose aus/ein - und es funktioniert wieder.

Man kann dort die App + Typ oder auch nur einen Typ (also wohl eine Device Class) auswählen:

Funktioniert bei mir ganz zuverlässig.

1 Like

Ich habe für diverse Geräte jeweils einen Flow zur Überwachung der Funktion. Für Kontaktschalter sieht er bspw. wie folgt aus:

Der Intervall ist individuell festzulegen. Eine bessere Möglichkeit ist mit bisher leider nicht eingefallen. Immerhin kann man nachvollziehen, wie häufig eine Gerät ausgelöst hat. Erschreckend, wie oft eine Haustür geöffnet wird :slight_smile:

1 Like

Schöne Idee - Danke fürs Teilen :slight_smile:

Hallo zusammen

Ich habe für das ein Script geschrieben (in Zusammenarbeit mit der Matrix :wink:)

=> das Script wird via Advanced Flow jeden Abend ausgeführt und gibt mir den entsprechenden Output wo ich was prüfen oder Batterie wechseln sollte

  • Neben der Batterie wir geprüft, wann das letzte Mal ein Wert rapportiert wurde
  • Hier bin ich fan der Shelly Blu Door Window sensoren, da die Signalstärke immer wieder gemeldet wird und ich somit weis, das Gerät ist “da”
    → geht z.B. bei einfacheren Sensoren nicht, da muss ich dann halt prüfen gehen (aber immer noch besser als wenn das Gerät keine Batterie hat und eh nichts meldet) :smiley:

Nachtrag: neue Devices sind dann auch mit dabei :wink:

Script (vereinfacht ohne meine Custom Rules)

/*
 * --- Homey Device Monitor & Battery Checker v1.0 ---
 * Editor: Rick_D
 * * BESCHREIBUNG:
 * Überwacht Homey-Geräte auf Erreichbarkeit und Batteriestand. 
 * Das Script erstellt eine übersichtliche Tabelle im Log und setzt Homey-Tags für Flows.
 * * * VERFÜGBARE REGEL-OPTIONEN (in DEVICE_RULES):
 * - notReportingDays:  Erlaubte Tage ohne Meldung (überschreibt Global).
 * - batteryThreshold:  Batteriewarnung in % (überschreibt Global).
 * - excludeBattery:    (true) Batterieprüfung für dieses Gerät deaktivieren.
 * - onlyCheckBattery:  (true) Ignoriert Erreichbarkeit, prüft nur Batterie.
 * - excludeAll:        (true) Gerät komplett ignorieren.
 * * * BEDEUTUNG DER REGEL-KÜRZEL (Spalte 'Rgl'):
 * - ID: Treffer über die Geräte-ID (Eindeutigste Zuordnung).
 * - NM: Treffer über den exakten Gerätenamen.
 * - PT: Treffer über ein Regex-Muster (Pattern).
 * - --: Keine Regel gefunden, globale Standardwerte werden angewendet.
 */

// --- KONFIGURATION ---

// Globale Standards (wenn keine spezifische Regel greift)
const NOT_REPORTING_THRESHOLD_HOURS = 24; 
const BATTERY_THRESHOLD_PERCENT = 30;     

const DEVICE_RULES = {
    // BEISPIEL: Match via ID - Erlaubt längere Inaktivität (z.B. 7 Tage) & ignoriert Batterie
    "id:00000000-0000-0000-0000-000000000000": { 
        notReportingDays: 7, 
        excludeBattery: true 
    },
    
    // BEISPIEL: Match via Name - Niedrige Batterieschwelle (Warnung erst bei 10%)
    "name:Mein Sensor im Garten": { 
        batteryThreshold: 10 
    },
    
    // BEISPIEL: Match via ID - Nur Batterie prüfen (z.B. für Saison-Geräte)
    "id:11111111-1111-1111-1111-111111111111": { 
        onlyCheckBattery: true 
    },
    
    // BEISPIEL: Match via Pattern (Regex) - Schließt alle Geräte mit diesen Begriffen im Namen aus
    "pattern:/Licht|Plug|Relay|Virtual/i": {
        excludeAll: true
    }
};

// Filter für den Scan
const EXCLUDED_ZONES = ['n-a']; // Zonen in Kleinbuchstaben
const EXCLUDED_DRIVER_URI_PATTERN = /vdevice|nl\.qluster-it\.DeviceCapabilities|nl\.fellownet\.chronograph|net\.i-dev\.betterlogic|com\.swttt\.devicegroups|com\.gruijter\.callmebot|com\.netscan/i;
const INCLUDED_DEVICE_CLASS_REGEX = /sensor|button|washer_and_dryer|thermostat|remote|socket|lights|lock|other|bulb|vacuumcleaner|camera|windowcoverings/i;

// --- SKRIPTLOGIK ---

let allDevices = [];
let DevicesNotReporting = [];
let DevicesLowBattery = [];
let ExcludedDevicesAll = [];

const devices = await Homey.devices.getDevices();
const zones = await Homey.zones.getZones();
const zoneMap = Object.fromEntries(Object.values(zones).map(zone => [zone.id, zone.name]));

// Hilfsfunktionen für Formatierung
function formatDate(date) {
    if (!date || isNaN(date.getTime())) return "Unbekannt";
    return date.toLocaleString('de-DE');
}

function padRight(str, width) {
    str = String(str);
    return str.length >= width ? str.slice(0, width) : str + ' '.repeat(width - str.length);
}

function printRows(devArray) {
    if (devArray.length === 0) { console.log("Keine Einträge."); return; }
    let header = [
        padRight('#', 3), 
        padRight('Name', 30), 
        padRight('Update', 18), 
        padRight('Batt', 5), 
        padRight('Stat', 4), 
        padRight('Rgl', 3),
        'Geräte-ID'
    ].join(' ');
    
    console.log(header);
    console.log('-'.repeat(header.length + 36));
    
    devArray.forEach((d, i) => {
        console.log([
            padRight(i+1, 3), 
            padRight(d.name, 30), 
            padRight(d.lastUpdated, 18), 
            padRight(d.batt, 5), 
            padRight(d.status, 4), 
            padRight(d.ruleApplied, 3),
            d.id
        ].join(' '));
    });
}

// Haupt-Scan
for (const device of Object.values(devices)) {
    let customRule = {};
    let ruleApplied = '--';
    let isExcludedAll = false;

    // Matching (ID -> Name -> Pattern)
    if (DEVICE_RULES[`id:${device.id}`]) {
        customRule = DEVICE_RULES[`id:${device.id}`];
        ruleApplied = 'ID';
    } else if (DEVICE_RULES[`name:${device.name}`]) {
        customRule = DEVICE_RULES[`name:${device.name}`];
        ruleApplied = 'NM';
    } else {
        for (const key in DEVICE_RULES) {
            if (key.startsWith('pattern:') && new RegExp(key.substring(8)).test(device.name)) {
                customRule = DEVICE_RULES[key];
                ruleApplied = 'PT';
                if (customRule.excludeAll) isExcludedAll = true;
                break;
            }
        }
    }

    if (isExcludedAll) {
        ExcludedDevicesAll.push({ name: device.name, id: device.id });
        continue;
    }

    // Filterkriterien prüfen
    if (device.driverUri && EXCLUDED_DRIVER_URI_PATTERN.test(device.driverUri)) continue;
    const zoneName = device.zone ? zoneMap[device.zone] : null;
    if (zoneName && EXCLUDED_ZONES.includes(zoneName.toLowerCase())) continue;
    if (!device.class || !INCLUDED_DEVICE_CLASS_REGEX.test(device.class)) continue;

    // Erreichbarkeit prüfen
    let maxLastUpdatedTime = null;
    let isReporting = true;
    if (!customRule.onlyCheckBattery) {
        for (const cap of Object.values(device.capabilitiesObj || {})) {
            const time = new Date(cap.lastUpdated).getTime();
            if (time > (maxLastUpdatedTime || 0)) maxLastUpdatedTime = time;
        }
        const thresholdHrs = customRule.notReportingDays ? customRule.notReportingDays * 24 : NOT_REPORTING_THRESHOLD_HOURS;
        isReporting = (Date.now() - (maxLastUpdatedTime || 0)) < (thresholdHrs * 3600000);
    }

    // Batterie prüfen
    let batteryStatus = 'N/A';
    if (customRule.excludeBattery) {
        batteryStatus = 'EXCL';
    } else {
        const measuredPct = device.capabilitiesObj?.measure_battery?.value;
        const alarmBattery = device.capabilitiesObj?.alarm_battery?.value;
        const threshold = customRule.batteryThreshold !== undefined ? customRule.batteryThreshold : BATTERY_THRESHOLD_PERCENT;

        if (typeof measuredPct === 'number' && !Number.isNaN(measuredPct)) {
            batteryStatus = `${Math.round(measuredPct)}%`;
            if (measuredPct <= threshold) {
                DevicesLowBattery.push({ name: device.name, status: batteryStatus, id: device.id });
            }
        } else if (alarmBattery) {
            batteryStatus = 'ALARM';
            DevicesLowBattery.push({ name: device.name, status: 'ALARM', id: device.id });
        } else {
            batteryStatus = device.capabilities.includes('measure_battery') ? 'OK' : 'N/A';
        }
    }

    allDevices.push({
        name: device.name,
        id: device.id,
        lastUpdated: maxLastUpdatedTime ? formatDate(new Date(maxLastUpdatedTime)) : 'Keine Daten',
        batt: batteryStatus,
        status: isReporting ? 'OK' : 'NOK',
        ruleApplied: ruleApplied
    });

    if (!isReporting) {
        DevicesNotReporting.push(`${device.name} (ID: ${device.id})`);
    }
}

// --- AUSGABE ---

console.log(`\n--- MONITORING ZUSAMMENFASSUNG ---`);
console.log(`Geräte im Scan: ${allDevices.length} | Offline: ${DevicesNotReporting.length} | Batterie leer: ${DevicesLowBattery.length}`);
console.log(`----------------------------------\n`);

console.log(`[!] KRITISCH: Offline oder meldet nicht:`);
printRows(allDevices.filter(d => d.status === 'NOK'));

console.log(`\n[!] BATTERIE-WARNUNGEN:`);
if (DevicesLowBattery.length > 0) {
    DevicesLowBattery.forEach((d, i) => {
        console.log(`${padRight(i+1, 3)} ${padRight(d.name, 30)} Status: ${padRight(d.status, 6)} ID: ${d.id}`);
    });
} else {
    console.log("Keine.");
}

console.log(`\n[i] ONLINE & OK:`);
printRows(allDevices.filter(d => d.status === 'OK'));

console.log(`\n[x] IGNORIERTE GERÄTE (excludeAll via Pattern):`);
if (ExcludedDevicesAll.length > 0) {
    ExcludedDevicesAll.forEach((d, i) => {
        console.log(`${padRight(i+1, 3)} ${padRight(d.name, 30)} ID: ${d.id}`);
    });
} else {
    console.log("Keine.");
}

// Homey Tags setzen (für Flows)
await tag('notReportingCount', DevicesNotReporting.length);
await tag('lowBatteryCount', DevicesLowBattery.length);
await tag('InvalidatedDevices', DevicesNotReporting.join('\n'));
await tag('LowBatteryDevices', DevicesLowBattery.map(d => `${d.name} (${d.status})`).join('\n'));

return `Scan abgeschlossen: ${allDevices.length} Geräte geprüft.`;

Ausgabe Bsp:

könnt ihr kopieren und mit ein paar Iterationen an Eure Bedürfnisse anpassen :slight_smile:

→ ich habe viele custom rules (die wachsen ständig)

Flow

Der ist dann ganz einfach

→ ich arbeite mit Simple Sys Log und alles mit Notice kriege ich eine Push-Notification (normale Benachrichtigung würde natürlich auch gehen).

2 Likes

Läuft gut, vielen Dank.

Die Variante mit den Flows finde ich persönlich (wenn auch aufwändig) am elegantesten, da man dort nichts mit Scripts und so machen muss.

Ich habe das jetzt mal getestet, es werden aber nicht alle Geräte richtig ausgewertet. Ich habe z.B. einen Shelly WIFI-Button, der nicht funktionieren kann, weil ich weiß dass die Batterie leer ist. Der wird aber nicht als fehlerhaft angezeigt.

Ein Ikea-Timmerflotte Thread Temperatursensor steht in dem Flow gar nicht erst zur Auswahl.

Und kann man im “Dann” nicht irgendwie automatisch den Namen des fehlerhaften Geräts einsetzen, so dass man nur ein Dann Feld benötigt?

ich habe auch mit Flows gearbeitet - hatte dann aber viel zu viele Flows und die Script Variante finde ich sehr elegant → alles an 1 Ort / Ausschlüsse möglich / Custom rules. Weiter sind neue Geräte auch dabei und die Möglichkeit eines “hat X Tage keinen Wert gemeldet” geht sonst nur mit Zählern oder Vergleichswerten etc…

Wenn du ein Gerät vermisst musst du kurz in den Developer Tools schauen, ob die Klasse bei “Included_Device_Class” dabei ist

mein shelly wifi button wird korrekt erkannt

Die Werte bzw. der “Text” der betroffenen Geräte wird in die Tags geschrieben, welche du mit dem Advanced Flow dann auswählen kannst

hier im Auszug aus dem Flow

Mein Tipp zum Vorgehen

  • Script kopieren
  • Script testen
  • prüfen ob Geräte fehlen / Filter setzen etc… → geht mit Support von Homey Developer Tools
  • wenn alles ok dann ab in den Advanced flow und zu beliebigen Zeiten (bei mir täglich wenn ich nicht in den Ferien bin) ausführen lassen und die Meldungen schicken

Rausgefilterte Geräte werden sep. angezeigt in der Konsole (bei mir unten z.B. virtuelle Geräte)

Richtig gutes Skript! Hab das mal ausprobiert und die Darstellung ist super. Danke dafür :slight_smile: