ZoneMinder / zmeventnotification

Machine Learning powered Secure Websocket & MQTT based ZoneMinder event notification server
412 stars 128 forks source link

Sending Image Data to APNs/ZMNinja without exposing ZoneMinder Event Server #358

Closed jaredvacanti closed 3 years ago

jaredvacanti commented 3 years ago

Event Server version 6.1.5

Hooks version (if you are using Object Detection) hooks:6.1.5 pyzm:0.3.25

The version of ZoneMinder you are using: 1.34.22

What is the nature of your issue

Question

Details

I bought ZMNinja for iOS and set it up with a single ZM instance really quickly. It all works great. The docs for installation and usage are really complete. Thanks for that! It really makes a big difference in getting started.

Is there a way to get a picture in the ZMNinja iOS notification without exposing the Event Server machine to the WAN? Now it looks like the only customization is ZMES_PICTURE_URL. Is there a way to send the image data in the payload base64 encoded? Or, should I use an existing hook to post to a CDN? (Do I then just use the CDN url? Can I delete it after APNs accesses it? I don't know much about iOS notifications.)

There are probably multiple ways to get it done, I was wondering if you favored one of them over the others.

Thanks for your help and the project!

pliablepixels commented 3 years ago

Glad you like the solution.

There are multiple things combined in your question, so let's break them down:

a) For you to receive push notifications on your phone, the ES must be able to reach Google's FCM server (well, specifically, my cloud function that in turn calls Google's FCM server). That is how push notifications work in general - your app server (in this case ES) needs to tell Apple or Google there is a push to be sent. So one part of the answer is, your ES at the least needs outbound connection to my server.

b) Now let's talk about how zmNinja "registers" with the ES. In other words, how does the ES know it needs to send a push notification to your device? When zmNinja first starts, and you enable ES in its settings, it gets a unique push token from Google's FCM server. This push token ID is then sent to the ES via websockets (port 9000) so that the ES knows, when there is an event, it should send a push (via my cloud function -> via Google FCM -> Google/Apple -> device). Unless this token is saved, the ES will never be able to send your device a push. This means zmNinja needs to be able to access port 9000 of the ES. If zmNinja is inside a LAN, this will work.

c) Now here is the catch, that most people don't know. Once the token is registered in the ES, zmNinja doesn't strictly need to be able to connect to the ES as it already has the device token ID. It will get a push when an event happens. If however the token changes (Apple/Google decides this - when the app is deleted and reinstalled, the token changes. It also changes in other situations) it will need to connect to the ES again to update. This causes the following issue: Folks setup zmNinja with the ES when they first install, token registers. Then later, they block off access and push continues to work. The token changes later, and push stops working and people don't realize zmNinja can't connect to the ES.

So far, we've established when zmNinja actually needs to connect to the ES (port 9000 by default).

Now let's move to picture notifications.

When a push notification is sent to your device, it contains a URL. zmNinja has a handler that is called when your device gets the push notification. Its job is to parse the notification, get the image URL, and display it. This URL today is a ZM URL (not ES). This means zmNinja needs to be able to access ZM (the URL is specified in ZMES_PICTURE_URL) to retrieve the image.

a) Can this picture be embedded using a special field in push notification so that zmNinja doesn't need to fetch it? No. Because a push notification max size is 4K in android and I think 2K in iOS (this changes, but never enough to embed a full image), which is too small to embed an image, let alone base64 encode it which adds around 30-40% overhead. It is in apple/Google's interest to limit the size - these are super heavily used servers as almost every 2nd app in the world sends push for something.

b) Note here that the requirement is for zmNinja to be able to load the image by accessing ZM. So ZM doesn't really need to be on the WAN if zmNinja is not. If they are both in LAN, it will still work. However, the requirement is the image URL needs to be HTTPS and not using self-signed certs.

So what are the options? a) You could configure a VPN between zmN and your server. However in this case, your ES will still need to reach my cloud function to send out a push b) You expose port 9000 and your ZM port to WAN (this is what I use)

pliablepixels commented 3 years ago

closing as no response.