A script to check sensor last update

Hello everyone,
In my home set-up I have a bunch of Aqara sensors directly connected to a Homey that I use to monitor temperature and humidity (and for some of them, to trigger some automations based on those). Overtime they run out of battery and I only realised it when the automations don’t start or for the “passive” ones, only when I dive a little deeper in the Insights section.

My idea was to use a script to read at a regular interval (say every day) the last updated value of a given sensor and send a notification if this value is greater than one day (meaning that the sensor is battery dead or disconnected). I have a little experience with Javascript but not so much to dive into object and classes, so each of my attempt so far has miserably failed.

Can anyone be so nice to put me on the right path?
Thanks!

1 Like

Not a script, but maybe useable

First create a numeric variable, it’s used to store the temperature of the sensor every 24h at 7AM.
If the stored temp and the real sensor’s temp is equal → send notification.
While the chance of the temp being exactly equal to the temp 24h ago is very small.
If it is equal, the battery or connection is down.

If…
Time is 7AM
And…
Logics [[Stored24hrsTemp]] is exactly [[RealSensorTemperature]]
Then…
Send notification
Else…
Logics set [[Stored24hrsTemp]] to [[RealSensorTemperature]]

1 Like

In HomeyScript, there is an example script for getting sensor values:
example-printSensorValues.js

Hi Peter,
thanks for your suggestion! I was also considering using a flow and your logic definitely helps. (and works fine!) The only drawback is having to repeat it for every sensor. But I can live with that!

Thanks again!

1 Like

Hi JPe,
Thanks for your reply. I have been playing indeed with that script and I was able to get my home sensors’ values. But when it comes to the lastChanged property of the DeviceCapability class, it goes beyond my scripting skills and I don’t know how to cycle through and read those values. :frowning:

Don’t know ider, maybe @robertklep can give an example or hint?

Perhaps something like this:

const INVALIDATE_AFTER = 86400;

const invalidatedDevices = [];
for (const device of Object.values(await Homey.devices.getDevices())) {
  for (const capabilityObj of Object.values(device.capabilitiesObj || {})) {
    if (capabilityObj.lastUpdated && (Date.now() - new Date(capabilityObj.lastUpdated) > INVALIDATE_AFTER * 1000)) {
      invalidatedDevices.push(device.name);
    }
  }
}
await tag('InvalidatedDevices', invalidatedDevices.join(', '));
return invalidatedDevices.length != 0;

This will mark a device as invalid if it hasn’t updated for at least one day (86400 seconds). It will set a token that can be used in a notification.

2 Likes

Thanks Robert, a good starting point, however running it, (almost?) all my devices showed up as timed-out. Soo I added some console.log and found that per device, there are several loglines, here is an example from an active sensor:

sensor T & H Sensor Terras 2022-05-01T14:45:19.470Z 120299 ---ok----
sensor T & H Sensor Terras 2022-05-01T14:45:19.551Z 120222 ---ok----
sensor T & H Sensor Terras 2022-04-26T09:59:52.787Z 449246990 >NOK<
sensor T & H Sensor Terras 2022-03-15T02:43:00.909Z 4104258872 >NOK<

looks like memory pollution for me.

There isn’t a per-device “last updated” field so it has to check every capability of each device, that’s why you get multiple logs per device.

Right now, it will mark a device as “invalid” if one of the capabilities is older than 1 day, but I guess it makes more sense if it only invalidates a device is all of the capabilities are too old:

const INVALIDATE_AFTER = 86400;

const invalidatedDevices = [];
for (const device of Object.values(await Homey.devices.getDevices())) {
  if (! device.capabilitiesObj) continue;
  let count = 0;
  for (const capabilityObj of Object.values(device.capabilitiesObj)) {
    if (! capabilityObj.lastUpdated || (Date.now() - new Date(capabilityObj.lastUpdated) > INVALIDATE_AFTER * 1000)) {
      count++;
    }
  }
  if (count && count === Object.keys(device.capabilitiesObj).length) {
    invalidatedDevices.push(device.name);
  }
}
await tag('InvalidatedDevices', invalidatedDevices.join(', '));
return invalidatedDevices.length != 0;

EDIT: I modified the script to handle capabilities that don’t have a lastUpdated. These are now treated the same as an “expired” capability.

4 Likes

I have the same problem, and wanted a solution. You can check out my working script on GitHub if you’d like: GitHub - follestad/HomeyScripts

1 Like

I did it too here:

1 Like

You can also do this in flows with the Retrieve Insights card from the Device Capabilities app, per device.

1 Like

Found this thread when I started looking for a simple way to detect if a light was turned off by the wall switch. When I was using the Deconz app before, lights were automatically marked as unavailable (red exclamation mark) after a while when power got cut but with devices now added to Homey 2023 natively they just seem to be stuck in the last state without any visual clue that they are actually turned off completely.

Is there a small script that can get a list of lights and plugs that have not updated in the last x hours so I can create an alert when that happens?

I tried the scripts from @robertklep mentioned above (nice and compact), but it does not seem to return any devices on my HP23, even when I set the INVALIDATE_AFTER really low? I even asked ChateGpt and it came up with a script that also didn’t seem to do much (using device.lastReport). Is there an api version difference maybe?

BTW: It seems that the “Device has not reported for x hours” system card is not available in HP23 (yet)?

Yes ! Such useful feature and they will not include it. Mostly when you troubleshoot new setup.

Also I believe there is no way to detect that on Homey, regardless of script

1 Like

@JeeHaa @Sharkys
Like @Arie_J_Godschalk proposed last Januari, doesn’t this work on a Pro 2023? It’s done with this app.



Did some tests on a 2019, curious if this also works on a 2023 (which I don’t own);
I just disabled some apps here, and this is the result, only this is instant, and not after x hours of unavailability:

1 Like

Thanks, I also tried the DC app, but it looks like it only triggers on devices that get really unavailable in Homey (visible on the device icon in the App), which ahappens when you stop an app for example. It does not trigger on devices that have stopped reporting in x hours, which is what happens for Zigbee end devices that get disconnected or powered zigbee devices that have power cut off. Unfortunately Homey will happlicy show such a device as if it’s fine until you try to control it.

So I am looking for a script to get the “Lastupdated/Lastreported” property of a powered device in HP23 to make an educated guess that it is probably offline if it has not reported in an hour or so. As mentioned, it looks like the script of @robertklep does that, but somehow I just don’t get the expected results on HP23.

I am trying to modify the script so it iterates through all the devices, checks if any of the capabilities has recently updated and return only devices for which that is not the case. Unfortunately I am not familiar with javascipt, so it’s a bit of a struggle but I think I will get there eventually. :slight_smile:

1 Like

Owwww yeah, didn’t think of that. ‘Quiet’ devices are not marked as unavailable indeed :hugs:

To really solve the quiet sensors: As an example, on a Homey 201x f.i., Aqara sensors don’t get along with Ikea lights. I then got the issue with Aqara’s not reporting anymore after a while.
When I didn’t have Ikea lights, or when I replaced them with Lidl lights, I had no issue at all with the Aqara’s.

OK, based on Roberts script I came up with this snippet. It seems to work as intended for my Lights and Plugs (filtered by device name or class). Works for sensors too, but that may require a much higher threshold.

//GetDevicesNotReporting
const NotReportingThreshold = 120; /// Trigger when not reported in the last x minutes

let DevicesNotReporting = [];
let devices = await Homey.devices.getDevices();
for (const device of Object.values(devices)) {

  // Filter devices to check
  //if (device.driverUri.match('vdevice')) continue; // Exclude Virtual Devices
  if (!device.name.match('Lamp|^PC')) continue; // Include by device name
  //if (!device.class.match('light|socket')) continue; // Include device types
  //if (device.class.match('sensor|button|remote|socket')) continue; // Exclude device types
  //log(device)

  // Any capability updated recently? Set LastSeen to most recent update.
  IsReporting = false
  LastSeen = 1000000
  for (const capabilityObj of Object.values(device.capabilitiesObj)) {
    let lastUpdated = new Date(capabilityObj.lastUpdated)
    let timeSinceReport = Math.floor((Date.now() - lastUpdated)/1000/60) // Minutes since last update
    if (timeSinceReport < LastSeen){LastSeen = timeSinceReport }
    //log('LastUpdated: '+ device.name + ' ' + capabilityObj.id + ' ' + timeSinceReport + 'min')
    if (timeSinceReport < (NotReportingThreshold)) { IsReporting = true }
  }

  // Add to list if not reported 
  if (!IsReporting) {
    DevicesNotReporting.push(device.name + ' [' + LastSeen + 'm]');
    log('NOK: ' + device.name + ' - ' + device.class + ' - ' + device.virtualClass + ' [' + LastSeen + 'm]')
  } else {
    log('OK: ' + device.name + ' - ' + device.class + ' - ' + device.virtualClass + ' [' + LastSeen + 'm]')
  }

}
//log(DevicesNotReporting.length)

// Output for script AND card
await tag('DevicesNotReporting', DevicesNotReporting.join(', '));
return DevicesNotReporting.length != 0;


Use it with an AND Homeyscript card:

4 Likes

Thank you, a slight modification which will report devices with last update time stamps in CET time zones, in EU format, 24 hours and every device separately on it’s row… also case in-sensitive examples for filters, adjust it as you need.

Please note that NotReportingThreshold unit is seconds.

Latest update - A script to check sensor last update - #25 by Sharkys

Output example :

obrazek

2 Likes

Ah, so that’s the trick for case insensitivity! I use a lot of PowerShell, and although it all looks very similar, small things like that can be frustrating. :wink:

Unfortunately it looks like my Linkind Tunable Zigbee light bulbs (Amazon) do not report as frequently as expected, sometimes as much as >600 minutes since last update for example. Most of the time they do get updated within an hour or so, even if they are not controlled. Maybe I can find a way to send a command to these devices (maybe on/off identical to actual state).

Note: I have added them as generic Zigbee devices in Homey without a dedicated App which works fine, but means that they show up as a virtual device. I am pretty sure that in Deconz, they also got polled every 5m or so, and were marked unavailable pretty fast. ideally it should not take >1h.

Does this also happen for others with Zigbee Lights when using a dedicated App, do you also see LastUpdated of hours ago sometimes?