Skip to content

feat: add ntfy integration #2900

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: dev
Choose a base branch
from
Open

feat: add ntfy integration #2900

wants to merge 15 commits into from

Conversation

itzTheMeow
Copy link

@itzTheMeow itzTheMeow commented Apr 18, 2025


Homarr

Thank you for your contribution. Please ensure that your pull request meets the following pull request:

  • Builds without warnings or errors (pnpm build, autofix with pnpm format:fix)
  • Pull request targets dev branch
  • Commits follow the conventional commits guideline
  • No shorthand variable names are used (eg. x, y, i or any abbrevation)

WORK IN PROGRESS

Adds widget to show ntfy notifications.

  • Adds widget to show notifications from notification-based integrations.
  • Adds new integration category notifications.
  • Adds ntfy integration for notifications.
  • Adds new "every 30 seconds" cron timer.

Preview:
image

I attempted to make the notifications widget accessible to other notification services such as gotify, however not all services use topics to define the notification locations so it may need reworking if another service is to be supported.

The ntfy integration supports authentication or no authentication. To test the URL can be set to https://ntfy.sh and access token left unset. Some example topics to subscribe to: mytopic, test
To publish messages to these topics, use https://ntfy.sh/mytopic

Additional notes:
I set the cron timer for these to 30 seconds to avoid spamming the API with requests every 5 seconds (was getting ratelimited).

The integrations selector allows for multiple ntfy integrations to be selected for the same widget. The selected topics are shared between both instances so most likely one or the other will not have a specified topic.

Copy link

deepsource-io bot commented Apr 18, 2025

Here's the code health analysis summary for commits 240bf16..462e060. View details on DeepSource ↗.

Analysis Summary

AnalyzerStatusSummaryLink
DeepSource JavaScript LogoJavaScript❌ Failure
❗ 1 occurence introduced
View Check ↗

💡 If you’re a repository administrator, you can configure the quality gates from the settings.

@itzTheMeow
Copy link
Author

Note:
The icon here (new integration dropdown) is cut off, due to the way its shaped. Is there a way to fix this or should I just leave it?
image

@ajnart
Copy link
Contributor

ajnart commented Apr 19, 2025

Note: The icon here (new integration dropdown) is cut off, due to the way its shaped. Is there a way to fix this or should I just leave it? image

I'd say don't bother with it for now, we'll review the code and once every's good we can look into that

@ajnart
Copy link
Contributor

ajnart commented Apr 19, 2025

Don't forget to mark the PR as ready for review when you're done ;)

@itzTheMeow
Copy link
Author

My config for the screenshot (left to right, top to bottom):

Integrations:
ntfy: https://ntfy.sh - No Token
main: https://ntfy.xela.codes - Auth Token Provided (my personal ntfy instance)

Widgets:
main Selected
meow-backups Topics

ntfy Selected
test, mytopic Topics

ntfy Selected
No Topics

ntfy Selected
mytopic Topics

@itzTheMeow itzTheMeow marked this pull request as ready for review April 19, 2025 08:45
@itzTheMeow itzTheMeow requested a review from a team as a code owner April 19, 2025 08:45
@manuel-rw manuel-rw added the enhancement New feature or request label Apr 22, 2025
Copy link
Member

@manuel-rw manuel-rw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for this contribution. Let me know if you have any questions

@manuel-rw
Copy link
Member

Hi @itzTheMeow , do you need further input from us or what is the status of this?

@itzTheMeow
Copy link
Author

Hey, currently on vacation so I haven't had time to work on this. It's still in my plans.

@manuel-rw
Copy link
Member

No worries, thanks for the update. Enjoy the vacation

@manuel-rw
Copy link
Member

Hi @itzTheMeow , any updates? :)

@itzTheMeow
Copy link
Author

Sorry, been really busy. I'll do this after the weekend.

@Meierschlumpf
Copy link
Member

No problem, we only wanted to now if you are still interested in working on this. Otherwise we would either have dismissed it or implemented it ourself.

@itzTheMeow itzTheMeow requested a review from manuel-rw May 30, 2025 05:18
@itzTheMeow
Copy link
Author

This actually works quite nicely, you can combine different URLs and topics in the same notification widget.
image
You can still provide multiple topics (for ntfy specifically) by comma-separating them.

@manuel-rw manuel-rw requested a review from a team June 6, 2025 19:28
const integrationInstance = await createIntegrationAsync(integration);
return await integrationInstance.getNotificationsAsync();
},
cacheDuration: dayjs.duration(30, "seconds"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's this a bit too aggressive? In my opinion, 5 minutes should be plenty!
Remember, Homarr will fetch even if there is no user "active", meaning that we need to be careful with setting too low intervals.

Copy link
Member

@manuel-rw manuel-rw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, @homarr-labs/maintainers please add a second review.

Copy link
Member

@Meierschlumpf Meierschlumpf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the contribution ❤️
Looks good beside a few small things

await fetchWithTrustedCertificatesAsync(url, { headers: this.getHeaders() })
.then((response) => {
if (!response.ok) {
throw new Error(response.statusText);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use the ResponseError instead

})
.catch((error) => {
if (error instanceof Error) {
throw new Error(error.message);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest to just throw the existing error, so just throw error instead of throw new Error(error.message)

export const ntfyNotificationSchema = z.object({
id: z.string(),
time: z.number(),
event: z.literal("message"), // we only care about messages
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do I understand correctly, that we let the parsing fail if the event is different than message? IMO it would make more sense to just have event a string then and check in the integration that the event is message. Then we could also always throw a ParseError if there is an issue with parsing (just using parseAsync would be enough because of the existing event handler in the base integration for zod)

Comment on lines +98 to +100
<Text size="sm" c="dimmed">
{date}
</Text>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you could use the useTimeAgo hook that automatically updates the timestamp. Maybe you can add a second argument that specifies the update frequency and make it a longer time between updates here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants