# \[HomeyScript\] Timeline Wrangler

**URL:** <https://community.homey.app/t/homeyscript-timeline-wrangler/90714>\
**Category:** Tutorials\
**Tags:** homeyscript, homey-pro\
**Created:** [October 1, 2023, 8:31pm UTC](https://community.homey.app/t/homeyscript-timeline-wrangler/90714 "2023-10-01T20:31:04Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![Caseda](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/caseda/32/138695_2.png) [@Caseda](https://community.homey.app/u/Caseda)\
**Post date:** [October 1, 2023, 8:31pm UTC](https://community.homey.app/t/homeyscript-timeline-wrangler/90714/1 "2023-10-01T20:31:04Z")

</div>

## Timeline Wrangler

As the Community App Store has been offline for a while making the Timeline Manager² app unavailable, and seen several people that want to maintain their notification timeline on a occasional basis I’ve tried to make a HomeyScript that can do exactly that.

### How to use:

The only thing you need is an **Homey Pro** (any version, besides Homey (Cloud)) and the app [HomeyScript](https://homey.app/a/com.athom.homeyscript).

When you have that app installed you can go to the [Web GUI =\> Scripts](https://my.homey.app/scripts) and click on “New HomeyScript” on the bottom-left of the page.  
A new script will get created and you can give it a name (not mandatory, just easier to find it back later on), “Timeline Wrangler” is a good example 😉  
Then all you need to do is copy and paste the **entire** script below (118 lines) into the top field, and press the “Save” button.

### How to activate the script:

You can activate this script with the “Test” button or from any flow however you want, just pre-specify the code’s settings in the top of the code to whatever you want and activate the script.

Alternatively you can also use the “Code” flowcard of HomeyScript in Advanced Flows, it will return a `[string]` what it has done.

Example Flow(s):

 ![flow example](https://us1.discourse-cdn.com/flex025/uploads/athom/original/3X/1/e/1e9458598e87d9ff776b919bafa172ff72a6d0af.jpeg)

### Changelog:

- v1.4 (August 10, 2024):
  - Fixed: Fix for empty name categories

- v1.3 (October 2, 2023):
  - Fixed: Fix for nameless categories

- v1.2 (October 2, 2023):
  - Added: “Contains Text” filter

- v1.1 (October 1, 2023):
  - Fixed: how hours are looked at

- v1.0 (October 1, 2023):
  - Initial release

### Disclaimer:

Use this script on your own discretion, if the “deleteNotifications” setting is true, it **will** delete the notifications it mentions.

Please do keep the thread clean, any _non related_ replies will get deleted!  
If you have (general) questions about HomeyScript or the used Web API, then this is **not** the thread for that.

## The Script:

```auto
// Timeline Wrangler v1.4

// ==================== User Settings ====================
const deleteNotifications = false; // If "true" = actually removes notifications, if "false" = display only
const daysOld = 5; // 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 = 1000; // The maximum amount of notifications that stay, anything more will get deleted
const containsText = ''; // 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:apps',
  'homey:manager:backup',
  'homey:manager:energy',
  'homey:manager:experiments',
  'homey:manager:flow',
  'homey:manager:presence',
  'homey:manager:safety',
  'homey:manager:updates',
  'homey:manager:users',
  'homey:manager:zigbee',
];

// ==================== 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') && owners[key].uriObj.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).';
}

return 'No notifications were (actually) removed';

```

---

<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 2, 2023, 5:30pm UTC](https://community.homey.app/t/homeyscript-timeline-wrangler/90714/2 "2023-10-02T17:30:38Z")

</div>

Hey that’s awesome, I was under the impression this was only possible in the web api playground.

(Pro 2019 v8.1.6)

One thingy, it shows my categories correctly, but I also got an error when I set:

```auto
const showCategories = true;

```

Although it runs fine when I set it to ‘false’ again.

```auto
❌ Script Error
⚠️ TypeError: Cannot read properties of undefined (reading 'length')
    at Timeline_Wrangler.js:44:68
    at Array.forEach (<anonymous>)
    at Timeline_Wrangler.js:43:27
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async Timeline_Wrangler.js:40:3
    at async HomeyScriptApp.runScript (/app.js:495:22)
    at async Object.runScript (/api.js:30:22)

```

.

> [@Caseda](#):
>
> Added: “Contains Text” filter

Great! I was about to request it 😜 😍

---

<div class="post-metadata">

**Author:** ![Caseda](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/caseda/32/138695_2.png) [@Caseda](https://community.homey.app/u/Caseda)\
**Post date:** [October 2, 2023, 6:03pm UTC](https://community.homey.app/t/homeyscript-timeline-wrangler/90714/3 "2023-10-02T18:03:59Z")

</div>

> [@Peter\_Kawa](#):
>
> (Pro 2019 v8.1.6)

~~Something must have changed in the later updates then, as there was a SDKv3 update in v10 of the older Pro’s. 🤔 what are the rest of your settings?~~  
Or do you perhaps have one of those weird nameless categories if you go to the settings of notifications?

EDIT: That probably is the issue, could you try v1.3? There it should be accounted for any nameless categories.

---

<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 2, 2023, 9:26pm UTC](https://community.homey.app/t/homeyscript-timeline-wrangler/90714/4 "2023-10-02T21:26:51Z")

</div>

Oh yes, I indeed have one nameless category!  
No clue how it got there.

✅ Yup, the error is gone with v1.3, thanks!

AND this is neat, I now know from the script results what the nameless category is from:

```auto
- Unkown Name | 'homey:app:nl.onzewifi.timelinemanager2'

```

(it can’t be a coincidence it’s from the Timeline manager ² app 🤭 )

This is also good news for everyone with the infamous nameless categories on the Pro 2023 to find out what those are from.

---

<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:06am UTC](https://community.homey.app/t/homeyscript-timeline-wrangler/90714/5 "2023-10-27T01:06:02Z")

</div>

Some flow ideas if you like.

### 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

> **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:** ![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 30, 2023, 6:48pm UTC](https://community.homey.app/t/homeyscript-timeline-wrangler/90714/6 "2023-10-30T18:48:32Z")

</div>

Not sure if it is ‘too much’, but I’d like to share my interactive timeline cleanup, which uses this [Telegram app](https://community.homey.app/t/app-pro-telegram/66622), to be able to select pre-defined phrases as answer, or by entering a phrase to match certain timeline entries.  
The timeline entries which contain the answer text , are to be deleted.

 ![Screenshot from 2023-10-30 19-33-24](https://us1.discourse-cdn.com/flex025/uploads/athom/original/3X/d/c/dc7beae1baa814dcdac83f136206343cc92ee718.png)

- Impression of the Telegram question + answer system, via app settings:  

- The flow asks a question via Telegram, and it comes with selectable answers  
 ![Screenshot from 2023-10-30 19-35-29](https://us1.discourse-cdn.com/flex025/uploads/athom/original/3X/4/d/4d3ee7fdba4d6ff2fc9519c5a8c7b901f0d6e7c2.png)

- And the same for a free-to-answer question:  
 ![Screenshot from 2023-10-30 19-41-43](https://us1.discourse-cdn.com/flex025/uploads/athom/original/3X/4/6/46a44eef639033dad50d3b3175e3e0788e39e718.png)

- Feedback  
 ![Screenshot from 2023-10-30 19-36-29](https://us1.discourse-cdn.com/flex025/uploads/athom/original/3X/a/4/a4c7b644409d09faf1d534ae1c67a2e923b813e3.png)

- Code used in the Homeyscript flow cards:

> **Click me**
>
> ```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 = true; // 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:** ![spkesDE](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/spkesde/32/54352_2.png) [@spkesDE](https://community.homey.app/u/spkesDE)\
**Post date:** [October 30, 2023, 9:05pm UTC](https://community.homey.app/t/homeyscript-timeline-wrangler/90714/7 "2023-10-30T21:05:43Z")

</div>

But why do you use two Telegram bots? 😃

---

<div class="post-metadata">

**Author:** ![Le\_Cactus](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/le_cactus/32/9727_2.png) [@Le\_Cactus](https://community.homey.app/u/Le_Cactus)\
**Post date:** [February 10, 2024, 9:14am UTC](https://community.homey.app/t/homeyscript-timeline-wrangler/90714/8 "2024-02-10T09:14:18Z")

</div>

> [@Peter\_Kawa](#):
>
> This is also good news for everyone with the infamous nameless categories on the Pro 2023 to find out what those are from.

This seems to occur when notifications are created by apps that have spaces in their names like “MQTT Broker”, “Device Capabilities” and as you found out also “\< Timeline Manager² \>”.  
When a notification is create from one of those apps, the category isn’t created correctly;

```auto
{
	"homey:app:nl.onzewifi.timelinemanager2": {
		"enabled": true,
		"push": false,
		"name": null,
		"size": 0,
		"uriObj": {
			"name": null
		}
	}
}

```

As far as I know the developers can’t do anything about it, it’s something in the API. These categories aren’t showing in the list of available categories when you try to clean the timeline from the Homey app (well at least from the 10.3.0-rc.12 firmware, but possible with other versions too).

I’ve updated the HCS version of \< Timeline Manager² \> to work around this problem.

---

<div class="post-metadata">

**Author:** ![AltonV](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/altonv/32/130229_2.png) [@AltonV](https://community.homey.app/u/AltonV)\
**Post date:** [August 9, 2024, 11:05pm UTC](https://community.homey.app/t/homeyscript-timeline-wrangler/90714/9 "2024-08-09T23:05:49Z")

</div>

I got this error by setting the variable ‘showCategories’ to true.

```auto
TypeError: Cannot read properties of null (reading 'length')
    at t-c.js:38:70
    at Array.forEach (<anonymous>)
    at t-c.js:36:27
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async t-c.js:33:3
    at async HomeyScriptApp.runScript (/app/app.js:364:22)
    at async Object.runScript (/app/api.js:30:22)

```

It was because the name of ‘homey:manager:google-assistant’ was null

I did fix it by changing “owners[key].uriObj.hasOwnProperty(‘name’)” on line 37 to ‘owners[key].uriObj.name’

---

<div class="post-metadata">

**Author:** ![Caseda](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/caseda/32/138695_2.png) [@Caseda](https://community.homey.app/u/Caseda)\
**Post date:** [August 10, 2024, 10:49am UTC](https://community.homey.app/t/homeyscript-timeline-wrangler/90714/10 "2024-08-10T10:49:23Z")

</div>

Hmm, an _empty_ name, curious how that managed to happen at all.  
But your way will also stop working if there is no “name” tag what the current check does, but i’ll fix it in the code.  
Thank you for noticing.

EDIT: Should be handled/fixed in v1.4.

---

<div class="post-metadata">

**Author:** ![TheKos](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/thekos/32/33969_2.png) [@TheKos](https://community.homey.app/u/TheKos)\
**Post date:** [December 18, 2024, 8:06pm UTC](https://community.homey.app/t/homeyscript-timeline-wrangler/90714/11 "2024-12-18T20:06:03Z")

</div>

Awsome, just what I needed, chapeau for this great code.

---

<div class="post-metadata">

**Author:** ![Ronwell](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/ronwell/32/46980_2.png) [@Ronwell](https://community.homey.app/u/Ronwell)\
**Post date:** [January 17, 2025, 4:41am UTC](https://community.homey.app/t/homeyscript-timeline-wrangler/90714/12 "2025-01-17T04:41:36Z")

</div>

Love your script, although I am trying to use the maximumnotificcations, I have set it to 250. but it deletes all the notifications. am I wrong in thinking that it should leave 250 of the most recent and delete the rest.  
my flow is when a timeline notification is sent run the script (with it set to 250.  
this way the timeline will not get above 250, am i correct

---

<div class="post-metadata">

**Author:** ![Caseda](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/caseda/32/138695_2.png) [@Caseda](https://community.homey.app/u/Caseda)\
**Post date:** [January 17, 2025, 11:10am UTC](https://community.homey.app/t/homeyscript-timeline-wrangler/90714/13 "2025-01-17T11:10:47Z")

</div>

The other filter(s) have an higher priority, so if you enabled any other option it will be 250 that are not part of the filter.

---

<div class="post-metadata">

**Author:** ![Ronwell](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/ronwell/32/46980_2.png) [@Ronwell](https://community.homey.app/u/Ronwell)\
**Post date:** [January 18, 2025, 3:20am UTC](https://community.homey.app/t/homeyscript-timeline-wrangler/90714/14 "2025-01-18T03:20:47Z")

</div>

I havn’t set any other filters  
with the maximum number option at 250, is that the most recent 250 or the oldest 250
