# Help reducing battery low warnings in flow

**URL:** <https://community.homey.app/t/help-reducing-battery-low-warnings-in-flow/92384>\
**Category:** Questions & Help\
**Created:** [October 21, 2023, 3:06am UTC](https://community.homey.app/t/help-reducing-battery-low-warnings-in-flow/92384 "2023-10-21T03:06:56Z")\
**Posts on this page:** 8\
**Page:** 2

<div class="post-metadata">

**Author:** ![DirkG](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/dirkg/32/118897_2.png) [@DirkG](https://community.homey.app/u/DirkG)\
**Post date:** [October 26, 2023, 1:28pm UTC](https://community.homey.app/t/help-reducing-battery-low-warnings-in-flow/92384/21 "2023-10-26T13:28:16Z")

</div>

Yes, but it’s the same with AF:

![image](https://us1.discourse-cdn.com/flex025/uploads/athom/original/3X/8/8/88f49d18b14ae2d323841779213a421608f3158d.png)

@Peter_Kawa, must “Power User” be enabled for this logic card? I think not.

---

<div class="post-metadata">

**Author:** ![Tiwas](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/tiwas/32/10066_2.png) [@Tiwas](https://community.homey.app/u/Tiwas)\
**Post date:** [October 26, 2023, 1:42pm UTC](https://community.homey.app/t/help-reducing-battery-low-warnings-in-flow/92384/22 "2023-10-26T13:42:51Z")

</div>

Here’s what it looks like in windows. It won’t allow { or } in the text box.  
 ![Animation](https://us1.discourse-cdn.com/flex025/uploads/athom/original/3X/e/a/ea3e96b5a13053b61cf053ce5e316cf88ac22071.gif)

---

<div class="post-metadata">

**Author:** ![Tiwas](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/tiwas/32/10066_2.png) [@Tiwas](https://community.homey.app/u/Tiwas)\
**Post date:** [October 26, 2023, 1:46pm UTC](https://community.homey.app/t/help-reducing-battery-low-warnings-in-flow/92384/23 "2023-10-26T13:46:01Z")

</div>

@Peter_Kawa 's solution worked fine under windows/chrome 🙂

---

<div class="post-metadata">

**Author:** ![DirkG](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/dirkg/32/118897_2.png) [@DirkG](https://community.homey.app/u/DirkG)\
**Post date:** [October 26, 2023, 1:49pm UTC](https://community.homey.app/t/help-reducing-battery-low-warnings-in-flow/92384/24 "2023-10-26T13:49:37Z")

</div>

This is the wrong logic card… 🤷🏼‍♂️

You use this one:  
 ![image](https://us1.discourse-cdn.com/flex025/uploads/athom/original/3X/5/e/5ed371db899e9cae471fb0133be19e87213186d4.png)

But you need this one like @Peter_Kawa showed already in his screenshots:

![image](https://us1.discourse-cdn.com/flex025/uploads/athom/original/3X/5/2/5295124f22c71d022d344c91dd922f84c75c7fd3.png)

---

<div class="post-metadata">

**Author:** ![Tiwas](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/tiwas/32/10066_2.png) [@Tiwas](https://community.homey.app/u/Tiwas)\
**Post date:** [October 26, 2023, 2:09pm UTC](https://community.homey.app/t/help-reducing-battery-low-warnings-in-flow/92384/25 "2023-10-26T14:09:05Z")

</div>

Yes, I tried that. It’s in the post above this 🙂

Thanks!

---

<div class="post-metadata">

**Author:** ![Peter\_Kawa](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/peter_kawa/32/173848_2.png) [@Peter\_Kawa](https://community.homey.app/u/Peter_Kawa)\
**Post date:** [October 27, 2023, 1:17am UTC](https://community.homey.app/t/help-reducing-battery-low-warnings-in-flow/92384/26 "2023-10-27T01:17:32Z")

</div>

> [@JanPieter88](#):
>
> Yes, the same with my robot-lawnmower that floods the timeline with ‘warnings’ about its battery every day. I wish it were possible to exclude certain devices’ energy warnings from the timeline.

You can file a request at homey.app/support, but I think chances are pretty low

But here’s a workaround 🙂

> [@\[HomeyScript\] Timeline Wrangler](https://community.homey.app/t/homeyscript-timeline-wrangler/90714/5):
>
> ### Auto remove certain ‘Low Battery’ notifications by Homey, most of the time annoying and useless when a device uses rechargable batteries, like robo mowers, and robo vacs
> 
> - This flow is triggered by new timeline notifications,
> - If it matches the phrase you enter as argument to this Homeyscript card,  
> ![Screenshot from 2023-10-27 03-18-53](https://us1.discourse-cdn.com/flex025/uploads/athom/original/3X/a/0/a0c5706532f0e6b531daf55af96c645c8d0a9bd4.png)  
> (_find the used script below_)
> - 
> - in this example: ` **My_Mower** is low on battery` (an exact part of the original message in the Timeline notification),
> 
> - _Then, it removes the timeline notification right away_  
> .
> 
> ![Screenshot from 2023-10-27 02-48-04](https://us1.discourse-cdn.com/flex025/uploads/athom/original/3X/b/0/b0864cb9ff148f2d7be13206e5b840b8e847ac35.png)
> 
> - Detail of how to enter the search phrase argument  
> ![Screenshot from 2023-10-27 02-46-52](https://us1.discourse-cdn.com/flex025/uploads/athom/original/3X/d/9/d9a3a9b660cde2e412de7c63ad804537df639b37.png)
> 
> #### The slightly adjusted script:
> 
> - 
> - the argument is used with line  
> `const containsText = args[0]; // Only deletes notifications that contains the specified text`
> 
> - 
> - The script returns the used argument along with the ‘flows removed’ message
> 
> - 
> - Note: the categories list is my own, but it is not used here
> 
> (original script by @Caseda)
> 
> > **show me the script**
> >
> > ```auto
> > // DEL timeline msgs ALL matching | Argument = searchphrase - Timeline_Wrangler v1.3p
> > 
> > // ==================== User Settings ====================
> > const deleteNotifications = true; // If "true": actually removes notifications, if "false": display only
> > const daysOld = 0; // Amount of days that need to have past before a notification is deemed old
> > const hoursOld = 0; // Amount of hours that need to have past before a notification is deemed old
> > const maximumNotifications = 250; // The maximum amount of notifications that stay, anything more will get deleted
> > const containsText = args[0]; // Only deletes notifications that contains the specified text, Empty ( '' ) = not used
> > const showCategories = false; // If "true" it will show all timeline categories you have and had in the past
> > const useCategory = false; // If "true" it will only remove from the specified categories below
> > 
> > // Separate categories by comma ( , )
> > const categories = [
> > 'homey:manager:flow',
> > 'homey:manager:presence',
> > 'homey:manager:apps',
> > 'homey:manager:updates',
> > 'homey:manager:zigbee',
> > 'homey:manager:energy',
> > 'homey:manager:backup',
> > 'homey:manager:users',
> > 'homey:app:com.athom.flowchecker',
> > 'homey:manager:security',
> > 'homey:app:nl.qluster-it.DeviceCapabilities',
> > 'homey:app:net.i-dev.betterlogic',
> > 'homey:app:com.spkes.telegramNotifications',
> > 'homey:manager:safety',
> > 'homey:app:nl.nielsdeklerk.log',
> > 'homey:manager:experiments',
> > ];
> > 
> > // ==================== Don't change anything below here ====================
> > const removedNotifications = [];
> > const notificationCategories = [];
> > let amountNotifications = 0;
> > let amountKept = 0;
> > 
> > if (showCategories) {
> > await Homey.notifications.getOwners()
> > .then(owners => {
> > log(Object.keys(owners).length, 'Timeline Categorie(s):');
> > Object.keys(owners).forEach(key => {
> > if (owners[key].uriObj.hasOwnProperty('name')) {
> > log('-', owners[key].uriObj.name, (owners[key].uriObj.name.length <= 5) ? '\t\t\t|' : (owners[key].uriObj.name.length <= 12) ? '\t\t|' : '\t|', '\'' + key + '\'');
> > }
> > else {
> > log('- Unkown Name', '\t\t|', '\'' + key + '\'');
> > }
> > });
> > log('\n');
> > })
> > .catch(err => {
> > log(err);
> > return 'Getting categories failed';
> > });
> > }
> > 
> > await Homey.notifications.getNotifications()
> > .then(notifications => {
> > amountNotifications = Object.keys(notifications).length;
> > log(amountNotifications, 'Timeline Notification(s):');
> > 
> > Object.keys(notifications).forEach(key => {
> > const currentDate = new Date();
> > const dateCreated = new Date(notifications[key].dateCreated);
> > const timeDifference = (currentDate - dateCreated) / 1000;
> > const daysDifference = Math.floor(timeDifference / 86400);
> > const hours = Math.floor((timeDifference % 86400) / 3600);
> > const hoursDifference = Math.floor(timeDifference / 3600);
> > const minutes = Math.floor((timeDifference % 3600) / 60);
> > 
> > if (
> > ((containsText.length > 0 && notifications[key].excerpt.toLowerCase().includes(containsText.toLowerCase()))
> > && (daysDifference >= daysOld && hoursDifference >= hoursOld))
> > || maximumNotifications < amountKept
> > ) {
> > log('- Removing:', notifications[key].id, ' |', notifications[key].ownerUri, '|', notifications[key].excerpt, '(' + notifications[key].dateCreated + ' - ' + daysDifference + ' day(s), ' + hours + ' hour(s) and ' + minutes + ' minute(s) old)');
> > 
> > if (deleteNotifications) {
> > Homey.notifications.deleteNotification({id: notifications[key].id});
> > removedNotifications.push(notifications[key].id);
> > }
> > }
> > else if (
> > ((containsText.length === 0 && useCategory)
> > && (categories.includes(notifications[key].ownerUri))
> > && (daysDifference >= daysOld && hoursDifference >= hoursOld))
> > || maximumNotifications < amountKept
> > ) {
> > log('- Removing:', notifications[key].id, ' |', notifications[key].ownerUri, '|', notifications[key].excerpt, '(' + notifications[key].dateCreated + ' - ' + daysDifference + ' day(s), ' + hours + ' hour(s) and ' + minutes + ' minute(s) old)');
> > 
> > if (deleteNotifications) {
> > Homey.notifications.deleteNotification({id: notifications[key].id});
> > removedNotifications.push(notifications[key].id);
> > }
> > }
> > else if (
> > ((containsText.length === 0 && !useCategory)
> > && (daysDifference >= daysOld && hoursDifference >= hoursOld))
> > || maximumNotifications < amountKept
> > ) {
> > log('- Removing:', notifications[key].id, ' |', notifications[key].ownerUri, '|', notifications[key].excerpt, '(' + notifications[key].dateCreated + ' - ' + daysDifference + ' day(s), ' + hours + ' hour(s) and ' + minutes + ' minute(s) old)');
> > 
> > if (deleteNotifications) {
> > Homey.notifications.deleteNotification({id: notifications[key].id});
> > removedNotifications.push(notifications[key].id);
> > }
> > }
> > else {
> > log('- Keeping: ', notifications[key].id, ' |', notifications[key].ownerUri, '|', notifications[key].excerpt, '(' + notifications[key].dateCreated + ' - ' + daysDifference + ' day(s), ' + hours + ' hour(s) and ' + minutes + ' minute(s) old)');
> > amountKept++;
> > }
> > });
> > })
> > .catch(err => {
> > log(err);
> > return 'Script failed';
> > });
> > 
> > if (removedNotifications.length > 0) {
> > return 'Removed ' + removedNotifications.length + ' out of ' + amountNotifications + ' notification(s).\nUsed Argument: ' + args[0];
> > }
> > 
> > return 'No notifications were (actually) removed. \nUsed Argument: ' + args[0];
> > 
> > ```

---

<div class="post-metadata">

**Author:** ![JanPieter88](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/janpieter88/32/87145_2.png) [@JanPieter88](https://community.homey.app/u/JanPieter88)\
**Post date:** [October 27, 2023, 11:17am UTC](https://community.homey.app/t/help-reducing-battery-low-warnings-in-flow/92384/27 "2023-10-27T11:17:04Z")

</div>

> [@Peter\_Kawa](#):
>
> But here’s a workaround 🙂

👌!

---

<div class="post-metadata">

**Author:** ![Erikvl87](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/erikvl87/32/112784_2.png) [@Erikvl87](https://community.homey.app/u/Erikvl87)\
**Post date:** [November 23, 2025, 12:37pm UTC](https://community.homey.app/t/help-reducing-battery-low-warnings-in-flow/92384/28 "2025-11-23T12:37:40Z")

</div>

Hi all, just wanted to share my solution to this problem.

 ![image](https://us1.discourse-cdn.com/flex025/uploads/athom/original/3X/2/c/2cc7878cc0e117c1869e10b37ff397e8f3cbb42c.png)

The following HomeyScript is used inside a Flow to detect **only the first time** a sensor value drops **below a given threshold** (using the flow card “HomeyScript execute code with argument”).

It receives its parameters as a single argument string (e.g. `"Device:Zone:Sensor:Value:Threshold"`), splits it, and uses those parts to find the correct device, zone, and sensor capability.

The script first tries to find the correct device by matching three things: the device name, the zone name, and the current capability value. Using the capability value as an extra check helps avoid picking the wrong device when names or zones are duplicated.

It then fetches the device’s Insights logs for the past hour and looks up the sensor value from 5 minutes earlier.

To detect the first downward crossing, it compares these two values:

- Current value \< threshold
- Value 5 minutes ago ≥ threshold

If both are true, the sensor has just dropped below the threshold for the first time, and the script returns `true`. Otherwise, it returns `false`, preventing repeat triggers until the value rises above the threshold again.

```auto
// Detect first drop below threshold
if (!args[0]) throw new Error("Missing input.");

const [targetName, targetZoneName, targetSensorName, targetSensorValue, thresholdValue] = args[0].split(":").map(s => s.trim());

const sensorNameLower = targetSensorName.toLowerCase();
const sensorValueNum = Number(targetSensorValue);
const threshold = Number(thresholdValue);

const devices = await Homey.devices.getDevices();
const zones = await Homey.zones.getZones();

function findCapabilityByLocalizedName(device, localizedName) {
  return Object.entries(device.capabilitiesObj).find(
    ([capId, capObj]) => capObj?.title?.toLowerCase() === localizedName
  );
}

// Find first matching device with sensor and current value
const matchingDevice = Object.values(devices).find(device => {
  const zoneName = zones[device.zone]?.name;
  if (device.name !== targetName || zoneName !== targetZoneName) return false;

  const capEntry = findCapabilityByLocalizedName(device, sensorNameLower);
  if (!capEntry) return false;

  const [capId] = capEntry;
  const currentValue = Number(device.capabilitiesObj[capId]?.value);
  return currentValue === sensorValueNum;
});

if (!matchingDevice) {
  console.log("Return false: No matching device found.");
  return false;
}

const capEntry = findCapabilityByLocalizedName(matchingDevice, sensorNameLower);
const [capId] = capEntry;
const currentValue = Number(matchingDevice.capabilitiesObj[capId]?.value);

console.log(`Device: ${matchingDevice.id}, Current value: ${currentValue}`);

// ===== Get Insights log entries using 'lastHour' =====
let insightsRoot;
try {
  insightsRoot = await Homey.insights.getLogEntries({
    id: `${matchingDevice.uri}:${capId}`,
    resolution: 'lastHour'
  });
} catch (err) {
  console.log("Return false: Error fetching insights:", err.message);
  return false;
}

if (!insightsRoot?.values?.length) {
  console.log("Return false: No insight values available.");
  return false;
}

// Calculate steps back for 5 minutes
const stepMs = insightsRoot.step; // ms per step
const stepsBack = Math.ceil((5*60*1000) / stepMs);

const index = insightsRoot.values.length - 1 - stepsBack;
if (index < 0) {
  console.log("Return false: Not enough historical data for 5 minutes ago.");
  return false;
}

const value5minAgo = Number(insightsRoot.values[index].v);

console.log(`Value 5 min ago: ${value5minAgo}, Threshold: ${threshold}`);

// FIRST DROP CONDITION
if (currentValue < threshold && value5minAgo >= threshold) {
  console.log("Return true: Threshold crossed downward for the first time.");
  return true;
}

console.log("Return false: No first-time threshold drop detected.");
return false;

```

I am using this flow together with the [Todoist app](https://homey.app/en-nl/app/nl.jwienk.todoist/Todoist/) to create a task to replace the batteries.

[Previous page](https://community.homey.app/t/help-reducing-battery-low-warnings-in-flow/92384.md?page=1)
