Flow to Monitor devices and notify if not being live

code updated.. v2 of script out now.

Hello everyone!

I wanted to share a solution I’ve developed for monitoring devices/sensors connectivity in my smart home setup. The problem I faced was not knowing if a sensor was working and sending up-to-date information. I’m sure everyone has had the problem with devices showing “last update 5 months ago” without knowing it.

This advanced flow has a time-based trigger, a HomeyScript, and sends notifications to my phone. Based on the criticality of the sensors/devices being monitored, I adjust the runtime on the flow trigger and what devices to monitor.

I have seen other solutions to the same problem on this forum, but I have found them a bit complicated to implement. If anyone else is in the same situation as me, feel free to test this flow and give feedback/further develop it to make it better.

How to use the flow

Step 1: Create the script While using your computer, log in to your Homey portal and press “Web app”. In the left-side banner, click “HomeyScript”. Then click “+ New HomeyScript”, give it a name like Device Monitor , and paste in the code below.

The best part is, you only have to do this once! You won’t need to edit the script itself again.

GitHub repo:
https://github.com/2re1million/homeydevicemonitor/blob/main/homeyDeviceMonitor.js

Full Code:
/*
=== Homey Device Monitor Script ===

* This script is configured directly from your Homey Flow using the "Arguments" field.
* This makes it highly reusable for different monitoring needs without editing the code.
*
* Example Argument for critical sensors:
* { "MAX_TIME_SINCE_UPDATE": 8, "DEVICE_IDS_TO_SHOW": ["id-1", "id-2"] }
*
* Example Argument for all lights:
* { "MAX_TIME_SINCE_UPDATE": 72, "DEVICE_CLASSES_TO_SHOW": ["light"] }
*/

// --- DEFAULT CONFIGURATION ---
// These settings are used if no arguments are provided in the Flow.
const defaultConfig = {
  // Set to true to get a list of all devices and their IDs, useful for initial setup.
  SHOW_ALL_DEVICES: false,
  // Default classes to monitor. E.g., ['sensor', 'light']
  DEVICE_CLASSES_TO_SHOW: [],
  // Default IDs to monitor. E.g., ["id-1", "id-2"]
  DEVICE_IDS_TO_SHOW: [],
  // Default IDs to ignore.
  DEVICE_IDS_TO_IGNORE: [],
  // Default maximum time since last update (in hours).
  MAX_TIME_SINCE_UPDATE: 48,
};

// --- SCRIPT STARTS HERE ---

// Merge arguments from Flow with default config
const flowArgs = args[0] ? JSON.parse(args[0]) : {};
const CONFIG = { ...defaultConfig, ...flowArgs };


// Function to format the time since last update
function formatTimeSince(date) {
  if (!date) return 'Never';
  const seconds = Math.floor((new Date() - new Date(date)) / 1000);
  const intervals = [
    { label: 'month', seconds: 2592000 },
    { label: 'day', seconds: 86400 },
    { label: 'hour', seconds: 3600 },
    { label: 'minute', seconds: 60 },
  ];
  for (const interval of intervals) {
    const count = Math.floor(seconds / interval.seconds);
    if (count >= 1) {
      return `${count} ${interval.label}${count > 1 ? 's' : ''} ago`;
    }
  }
  return 'Just now';
}

// Main function to monitor devices
async function runDeviceMonitor() {
  let fullOutput = "";
  let conciseOutput = "";
  let issueCount = 0;
  const devices = await Homey.devices.getDevices();

  if (CONFIG.SHOW_ALL_DEVICES) {
    let allDevicesList = '--- ALL DEVICES ---\n';
    for (const device of Object.values(devices)) {
      allDevicesList += `\nName: ${device.name}\nClass: ${device.class}\nID: ${device.id}\n`;
    }
    log(allDevicesList);
    return true; // Stop execution after listing all devices
  }

  for (const device of Object.values(devices)) {
    // Filter by class, if specified
    if (CONFIG.DEVICE_CLASSES_TO_SHOW.length > 0 && !CONFIG.DEVICE_CLASSES_TO_SHOW.includes(device.class)) {
      continue;
    }
    // Filter by specific IDs, if specified
    if (CONFIG.DEVICE_IDS_TO_SHOW.length > 0 && !CONFIG.DEVICE_IDS_TO_SHOW.includes(device.id)) {
      continue;
    }
    // Ignore specified IDs
    if (CONFIG.DEVICE_IDS_TO_IGNORE.includes(device.id)) {
      continue;
    }

    if (!device.capabilitiesObj || Object.keys(device.capabilitiesObj).length === 0) continue;

    const mostRecentUpdate = Object.values(device.capabilitiesObj).reduce((latest, cap) => {
      const capDate = new Date(cap.lastUpdated);
      return capDate > latest ? capDate : latest;
    }, new Date(0));

    const timeSinceUpdateHours = (new Date() - mostRecentUpdate) / 3600000;

    if (timeSinceUpdateHours >= CONFIG.MAX_TIME_SINCE_UPDATE) {
      issueCount++;
      const lastSeen = formatTimeSince(mostRecentUpdate);
      fullOutput += `\n=== ${device.name} ===\nClass: ${device.class}\nLast Seen: ${lastSeen}\nID: ${device.id}\n`;
      conciseOutput += `- ${device.name} (seen ${lastSeen})\n`;
    }
  }

  if (issueCount === 0) {
    // No issues found, clear tags and throw an error to stop the flow
    await tag('full_report', '');
    await tag('concise_report', '');
    await tag('issue_count', 0);
    throw new Error('No devices with issues found. Flow stopped.');
  }

  // Issues were found, set tags and log the output
  const summary = `Found ${issueCount} device(s) needing attention.`;
  await tag('full_report', `${summary}\n${fullOutput}`);
  await tag('concise_report', `${summary}\n${conciseOutput}`);
  await tag('issue_count', issueCount);

  log(fullOutput);
  log(summary);

  return true;
}

// Run the monitor
return await runDeviceMonitor();

Step 2: Configure the script to suit your needs

Instead of editing the script file, you now configure everything in the Arguments field of the HomeyScript flow card.

First, you might need the IDs of your devices.

  • To get a list of all your devices and their IDs, create a temporary flow, run the script, and put this text in the Arguments field: { "SHOW_ALL_DEVICES": true } .
  • Check the HomeyScript log to see the output, and copy the IDs you need.

Now you can configure your real monitoring flow. Here are the two main ways:

Option 1: Monitor a few specific devices (best if you have a few devices you want to monitor closely)

  • In the Arguments field of the flow card, you’ll tell the script which devices to watch and how long to wait before notifying you.
  • Example: To monitor 3 specific sensors and get an alert if they haven’t been seen for 8 hours , you would paste this into the Arguments field:
{
  "MAX_TIME_SINCE_UPDATE": 8,
  "DEVICE_IDS_TO_SHOW": [
    "34sdf4-3433-332432-3sdfd",
    "dsfs3d-3433-332432-3sdfd",
    "55yytf4-3433-332432-3sdfd"
  ]
}

Option 2: Monitor whole groups of devices (best if there are many devices you want to monitor)

  • You can tell the script to monitor all devices of a certain class, like all your lights or all your sensors.
  • The different classes are: sensor , light , other , speaker , thermostat , socket , tv , blinds , remote .
  • Example: To monitor all of your lights and sensors and get an alert if any of them haven’t been seen for 72 hours , paste this into the Arguments field:
{
  "MAX_TIME_SINCE_UPDATE": 72,
  "DEVICE_CLASSES_TO_SHOW": [
    "light",
    "sensor"
  ]
}

If you want to monitor a class but exclude one or two specific devices, you can add their IDs to an ignore list like this:

{
  "MAX_TIME_SINCE_UPDATE": 72,
  "DEVICE_CLASSES_TO_SHOW": ["light"],
  "DEVICE_IDS_TO_IGNORE": ["id-of-light-to-ignore"]
}

ery important Sometimes the output of the code in the editor is Error . This is most likely because there are no faulty devices. The script is designed to do this to stop the flow from sending you a notification. You only get an alert when something is wrong!

Step 3: Create a new “Advanced Flow” and set it up like this This part is just like before. The key is to paste your configuration text from Step 2 into the Arguments field on the HomeyScript card.

It’s a time-based trigger card. Then the code card is: HomeyScript with “Run Script”. Select your new script from the list and paste your JSON configuration into the Arguments field. Then the notification card with you as a user, using the concise_report tag.

Step 4) Best Practices You can now easily create multiple flows for different needs without touching the script file.

For example, I have one flow that runs twice a day to check that my critical “water heater” and garage “dehumidifier” have been online in the last 8 hours.

Then I have a different flow with a weekly trigger to see if all devices with class sensor and light have been running for the last 168 hours.

2 Likes

Not to be an ass, but just FYI here’s a similar, less extended, script:

1 Like

Hi!

Thanks for the script, I set it up, but ran into a problem.
When I run the script, I get the output as shown in the picture.

Device “Snelk. Tafel” is an Ikea shortcut button (older style) which I use daily, although the script says it has been offline for 3 weeks.
Device “Wasmachine” is a socket which has been unplugged for some time, but I bought my (first) Homey two months ago, so 664 months seems a bit much… (I’m 372 months old :sweat_smile:)

Do you know what is happening here?

Script output:

Summary

=== Snelk. Tafel ===
Device Class: remote
Device ID: 01b871…
Batterij alarm: null (Last updated: 3 weeks ago)

=== PC Robbert ===
Device Class: socket
Device ID: 60cfe…
Aangezet: true (Last updated: 1 month ago)
Vermogen: null (Last updated: Never)
Accuniveau: 79 (Last updated: 1 month ago)

=== SP240 ===
Device Class: socket
Device ID: 66212…
Aangezet: true (Last updated: 1 month ago)
Energie: 0 (Last updated: 1 month ago)
Vermogen: 1 (Last updated: 1 month ago)
Stroom: 0.01 (Last updated: 1 month ago)
Voltage: 0 (Last updated: 1 month ago)

=== Wasmachine ===
Device Class: socket
Device ID: b5319a…
Aangezet: null (Last updated: Never)

=== Bewegingssensor ===
Device Class: sensor
Device ID: c51b…
Bewegingsalarm: false (Last updated: 2 days ago)

=== Achterdeur ===
Device Class: sensor
Device ID: dc7366…
Contact alarm: false (Last updated: 8 hours ago)
Accuniveau: 100 (Last updated: 1 month ago)

Device listing complete. 6 devices with issues found.

Total Devices Checked: 6

Devices with Issues: 6

———————————————————
:white_check_mark: Script Success

:leftwards_arrow_with_hook: Returned: true

Hi Robert, welcome.

To which post you’re replying to?

When you responded to my post:

I suggest to discuss script issues in the topic of the script I linked to.

I’m over 664 months old :rofl::rofl:
In Homey language, 664 months means, when no last seen value is found, it just returns 1-1-1970 as value.
(No clue who came up with that date).

Also, last_seen timestamp is not always the real last seen moment

Hi Peter,

I responded to 2re1million’s post, but thanks for your reply!
The date 1-1-1970 did ring a bell for me, that is the starting point for unix-time.
Unix time is measured in seconds after 1-1-1970. So I suppose when no last seen value is found, it returns “0”, which translates to 1-1-1970.
I think I’ll have to add that device to the ignore list then…

(Fun fact: If you change the time and date on an iPhone to 1-1-1970, it’ll brick the iPhone, because you are essentially setting the time to 0, and the OS can’t handle that")

1 Like

Ah yes, duh, the 0 in unix time!
It wouldn’t pop up in my head just then :sweat_smile:

That issue was solved 8 years ago already :stuck_out_tongue:

Hi !

The script is designed to look for the “last update” feedback from various devices and then pick the latest update to either send a notification or not.

It appears that your “Snelk. Tafel” is not generating a feedback signal from the device when being used/turned on/off. As a result, the script only relies on the feedback from “Battery alert,” which might only send signals when the battery is low or under certain conditions.

Since you use this device almost daily, you will quickly notice when it is not working. Therefore, you might want to add the device to the ignore list. :wink:

Note to self: The script works best with live feedback sensors, such as temperature sensors, etc.:stuck_out_tongue:

=== Snelk. Tafel ===
Device Class: remote
Device ID: 01b871…
Batterij alarm: null (Last updated: 3 weeks ago)