Skip to content
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

Forwarding Frigate events to Ntfy #635

Merged
merged 31 commits into from
Apr 18, 2023
Merged

Forwarding Frigate events to Ntfy #635

merged 31 commits into from
Apr 18, 2023

Conversation

amotl
Copy link
Member

@amotl amotl commented Apr 12, 2023

About

@sevmonster came up with an interesting use-case, aiming to subscribe to events from Frigate, and forward them to Apprise/Ntfy.

Details

A special property here is that there are two message types, for events and snaphots, where both will have to be correlated or synchronized somehow, so the solution must be stateful in one way or another.

What's inside

This patch brings in the configuration and adapter files into the examples directory (thanks, @sevmonster!), and may also include corresponding commits to unlock one thing or another. The corresponding README can be better consumed in the rendered variant.

References

@codecov-commenter
Copy link

codecov-commenter commented Apr 12, 2023

Codecov Report

Merging #635 (8434441) into main (640c78a) will increase coverage by 0.29%.
The diff coverage is 86.11%.

📣 This organization is not using Codecov’s GitHub App Integration. We recommend you install it so Codecov can continue to function properly for your repositories. Learn more

@@            Coverage Diff             @@
##             main     #635      +/-   ##
==========================================
+ Coverage   42.10%   42.39%   +0.29%     
==========================================
  Files          83       83              
  Lines        3826     3854      +28     
==========================================
+ Hits         1611     1634      +23     
- Misses       2215     2220       +5     
Flag Coverage Δ
unittests 42.39% <86.11%> (+0.29%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
mqttwarn/util.py 97.47% <75.00%> (-2.53%) ⬇️
mqttwarn/core.py 91.86% <84.61%> (-0.29%) ⬇️
mqttwarn/context.py 99.21% <100.00%> (+0.01%) ⬆️
mqttwarn/services/file.py 100.00% <100.00%> (ø)

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

amotl added 7 commits April 13, 2023 03:29
Use `cam-testdrive` as a camera name across the board.
Use `goat` as label across the board.
Dynamically compute name to snapshot file, based on `camera` and
`label`, using a filename template string like
`./var/media/{camera}-{label}.jpg`.
@amotl
Copy link
Member Author

amotl commented Apr 14, 2023

We have success. I had to make the changes I suggested in the PR to get the attachment to go through and prevent repeated notifications. But otherwise this is great.

-- #632 (comment)

Excellent! Thanks for adding your suggestions, I will try to bring them in. May I ask you to also share your walkthrough? It may be useful to spice up the README, specifically if you are using a setup based on Docker, which I did not take into consideration yet. And of course, this is the area where things may still want to be improved, with respect to your comments about addressing the filename/URL of the attachment.

Specifically, it would be so kind of you could provide walkthrough steps for me how to get the Ntfy part running. I've never used it, and it would be sweet to also bring it to the README.

Edit: I will try to get an Ntfy instance running later, based on 1, and bring it to the README.

Footnotes

  1. https://docs.ntfy.sh/install/#docker

click=f"https://frigate/events?camera={event.camera}&label={event.label}&zone={event.entered_zones[0]}",
attach=attach_filename,
)
return ntfy_parameters.to_dict()
Copy link
Member Author

@amotl amotl Apr 14, 2023

Choose a reason for hiding this comment

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

In order to quickly patch propagating the corresponding file reference to Apprise/Ntfy as filename instead of attach. Untested!

Suggested change
return ntfy_parameters.to_dict()
params = ntfy_parameters.to_dict()
params["filename"] = params["attach"]
del params["attach"]
return params

Copy link
Member Author

Choose a reason for hiding this comment

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

Forget about that unless we've made any progress on switching to HTTP PUT, see #635 (comment) ff.

@amotl
Copy link
Member Author

amotl commented Apr 14, 2023

I've just familiarized myself a bit more with Ntfy, and found that example how to attach a local file 1. It looks like Ntfy has all the machinery in place to accept and store files, and provide them again via HTTP, so this is indeed what should be leveraged here.

Is it only the current Ntfy implementation based on Apprise, which is not permitting to use the feature like that?

Footnotes

  1. https://docs.ntfy.sh/subscribe/cli/#attaching-a-local-file

@amotl
Copy link
Member Author

amotl commented Apr 14, 2023

I've not further explored this space more deeply yet, but I quickly want to share some pointers. Maybe this can contribute to improve the implementation, both for mqttwarn, and Apprise.

  • As far as I can see, the Apprise/Ntfy code currently implements the HTTP headers X-{Icon,Priority,Delay,Click,Email,Tags}. 1
  • However, the Ntfy client code, probably used for ntfy pub, also implements a few others, like X-{Message,Title,Actions,Attach,Filename,Cache,Firebase}. 2

So, could using X-Attach or X-Filename unlock anything sensible here, which would improve the situation? Currently, attach and filename are propagated as query parameters, that obviously can't get any payload through. That's why I found the comments on the relevant code of Ntfy interesting.

// WithAttach sets a URL that will be used by the client to download an attachment
func WithAttach(attach string) PublishOption {
	return WithHeader("X-Attach", attach)
}

// WithFilename sets a filename for the attachment, and/or forces the HTTP body to interpreted as an attachment
func WithFilename(filename string) PublishOption {
	return WithHeader("X-Filename", filename)
}

Footnotes

  1. https://github.com/caronc/apprise/blob/v1.3.0/apprise/plugins/NotifyNtfy.py#L502-L542

  2. https://github.com/binwiederhier/ntfy/blob/v2.3.1/client/options.go#L20-L103

@amotl
Copy link
Member Author

amotl commented Apr 14, 2023

TLDR; To actually upload data from the client, we should use the HTTP PUT request method offered by Ntfy.

Investigations

I've discovered the Python client implementation ntfy-wrapper. This is the corresponding code which implements the "attach" feature.

From the comments, we can learn that either attach or message can only be used exlusively. But wait, maybe this apparent drawback has already been remedied by using the X-Message HTTP header on the PUT request? Actually, when thinking about it once more, all parameters would have to go to the HTTP headers, because the body needs to be used for the attachment 1. Now, for my proposal, let's take it for granted that this will work well.

Proposal

a volume must be mounted that is served by a reverse proxy that Ntfy can query to download the image

It's sad because the whole setup will need more infrastructure then.

This effectively means I propose to use the "Attachments" feature of the Ntfy server to implement this feature. In this way, the client, in this case mqttwarn, will include the attachment within the request to Ntfy, so Ntfy does not need to resolve the resource from an external URL. The improvement here is that we do not have to care about how to make the image available per HTTP 2.

If desired, you may allow users to upload and attach files to notifications. To enable this feature, you have to simply configure an attachment cache directory and a base URL (attachment-cache-dir, base-url). Once these options are set and the directory is writable by the server user, you can upload attachments via PUT.

-- https://docs.ntfy.sh/config/#attachments

Please let me know if you see any problems with this approach.

Footnotes

  1. At least when not going down the Content-Type: multipart/mixed; route.

  2. With a subsequent iteration, we may even bring it to a level where the snapshot image would not have to be stored to the filesystem all, and everything would happen in-memory.

@sevmonster
Copy link

sevmonster commented Apr 14, 2023

Please let me know if you see any problems with this approach.

There aren't any problems with your approach necessarily, but there are potential blockers elsewhere. To reiterate/condense these:

  • Frigate does not send thumbnail through the MQTT event, so the file will have to be loaded from somewhere else if it is to be piped through mqttwarn and into Ntfy
  • Apprise as far as I can tell does not support passing body to Ntfy, and only the attachment parameter

I initially suggested sending the attachment through the request body in the OP of this issue, before I realized Apprise most likely didn't support it.

I see three options to improve the situation:

  • Pushing code/issues to the above blocking projects to fix the stated issues
  • Bundle the ntfy-wrapper package or similar into mqtt-full and create a new mqttwarn service to handle it
  • Use requests or similar and just write out the raw Ntfy HTTP API request into a mqttwarn service

@amotl
Copy link
Member Author

amotl commented Apr 14, 2023

Yeah, I am actually looking at the second or third option you've mentioned, other than also telling @caronc of Apprise fame about it.

Frigate does not send thumbnail through the MQTT event, so the file will have to be loaded from somewhere else.

Please help me understanding this detail. Personally, I've not discriminated between "snapshot image" and "thumbnail" yet, but I am not a user of Frigate. As far as I was concerned, images are arriving now in mqttwarn using the code we added to unblock receiving binary payloads, and that data was what I was looking at.

Please educate me if and how this thumbnail thing would be any kind of blocker. Would you like to scale down the image before forwarding it to Ntfy?

@sevmonster
Copy link

thumbnail is a property of the Event object in Frigate. It is just a thumbnail of the snapshot. Snapshots are still frames of detection events.

Frigate seems to be excluding thumbnail before sending the contents of the Event over MQTT, and that's why the current workaround is used with the snapshot endpoint—though so far it seems to be working pretty well.

I have no preference on whether the thumbnail or full snapshot (internally and previously known as clips) is used. Snapshots are usually very small in resolution anyway.

@amotl
Copy link
Member Author

amotl commented Apr 14, 2023

Excellent, thanks. By the way, have you been able to figure out in which order both messages (event vs. snapshot) will be published?

@sevmonster
Copy link

As far as I can see the image usually arrives after, though I can't confirm if it is actually being sent after or not. This seems to end up working fine in my environment, as the latency for Ntfy to send the HTTP request seems to take just longer than publishing and saving the new screenshot.

@amotl
Copy link
Member Author

amotl commented Apr 14, 2023

As far as I can see the image usually arrives after.

I've expected the same. However, this detail is important. What does mosquitt_sub -t 'frigate/#' reveal?

Comment on lines 53 to 56
[frigate/cam-testdrive/goat/snapshot]
targets = store-jpeg:cam-testdrive-goat
[frigate/cam-testdrive/squirrel/snapshot]
targets = store-jpeg:cam-testdrive-squirrel
Copy link
Member Author

Choose a reason for hiding this comment

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

This section may be condensed, depending on your taste, see Templated targets » Example 2.

Copy link
Member Author

Choose a reason for hiding this comment

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

It was needed to accompany this by a corresponding frigate_snapshot_decode_topic() user-defined function, see d8a2e45.

@amotl
Copy link
Member Author

amotl commented Apr 18, 2023

Hi again,

I had to take a step back and deactivate the snapshot -> attachment forwarding, in order to get a test suite in place with b6ac3fa, which now covers the rest of the story. Within that, we are running end-to-end integration tests with an Ntfy instance now, provided by Docker.

I will merge this now, also in order to be able to bring in GH-636. There will be a subsequent iteration, which will bring back the attachment forwarding. Please don't stop submitting your suggestions as we go, I love them.

With kind regards,
Andreas.

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

Successfully merging this pull request may close these issues.

Non-UTF-8 encoding causes error
3 participants