Closed requa3r0 closed 8 years ago
Glad you like the app. In my case, the sound is loud and clear (Sasmsung S2 and iPhone). There is something odd going on - you should be getting a zmninja icon on the status bar
@alager -- are you still using push notifications? Can you comment if the icon shows up on push? I only have one android device so its hard to tell whats going on with other devices.
I'll set it back up and let you know. Last time it was on I would get a bell shape for the notice. On Jan 9, 2016 5:35 PM, "Pliable Pixels" notifications@github.com wrote:
Glad you like the app. In my case, the sound is loud and clear (Sasmsung S2 and iPhone). There is something odd going on - you should be getting an android ion on the status bar
@alager https://github.com/alager -- are you still using push notifications? Can you comment if the icon shows up on push? I only have one android device so its hard to tell whats going on with other devices.
— Reply to this email directly or view it on GitHub https://github.com/pliablepixels/zmNinja/issues/135#issuecomment-170299587 .
Sounds good that everything should already be implemented. I was actually surprised that it wasn't. Ill try to set it up on a another device, just to rule out my rather hacked and customized HTC ONE M7 ;O)
I Installed ZmN on a stock HTC one mini. Precisely same deal. I only get a notification icon that looks like a B. I believe its the HTC stereo Beats-Audio icon, for the duration of the alarm sound. The sound volume is also just as low as on my HTC one M7. I also tested it with a total crap no name old tabled I had lying around, and it is the same.
Folks, my push server is down - I am on travel till 17th of Jan - not quite sure what happened, but till then you won't get push notifications on your phone in the background - sorry about that. Its supposed to restart but hasn't
@requa3r0 - okay, so you see the icon/ The "B" is actually a ninja icon of zmninja - two semi certicles with the "B" eyes
Your issue now is with blop.mp3 - okay, I'll investigate on return
@requa3r0 - I tested today. When in foreground, the sound is loud and clear. When in background the sound is very low. The problem actually can be resolved by setting notification volume - you need to go to your phone settings, then sound. You will see different settings for regular volume and notification volume. Make sure notification volume is set to as high as you need
Once you tweak this, your notification volume should be quite good.
Hi..
I have all volumes on full. Did also tick the setting to split ringtone and notifications in to 2 settings, and both are on full.
I still don't get any icon, and the sound is extremely low. The icon i saw on a HTC mini is not the correct one, but just a beats audio sound icon, for the short duration of the low sound.
With the current status, the event server makes no sense to use which is a real shame.
I Hope you can fix this at some point.
It would be quite handy as a proximity pre-alarm, to alert you already before some bust you back door ;O)
Okay, I think this is fixed now. You need to upgrade your event server to version 0.7 and make sure you set $useCustomNotificationSound
to 0. This will revert to using system default sounds. Proper process to upgrade: https://github.com/pliablepixels/zmeventserver#how-do-i-safely-upgrade-zmeventserver-to-new-versions
You also need to wait for the next version of zmNinja that should be out later this month.
I understand the issue has been closed, and that you have done quite a lot of work on the zmeventnotification.pl and app since.
I have updated everything to newest version and do get notifications when the app is running, but nothing when its closed. Still the same low sound & still no flags, icons banners or mexican mariachi bands.
For any one with the same problem, or for your general inspiration, here is a pretty good workaround.
I wanted to push notifications to my android with alarm on/off status, to check my little hack on my exiting alarm which is now coupled to the zoneminder through a small audoino board that is keeping an "eye" on the exiting alarms red diod and setting ZM accordingly automatically ;O)
Nevermind that..I bought the pushover app and have implemented it for this purpose.
I have now also implemented pushover into the zmeventnotification.pl, just to see how that would work.
When the app is running, ZMninja and pushover reacts pretty fast and at the same time.
When ZMninja is not running, i now still get pushover notifications and because i can set a priority, the alarm status is set to normal (following the system sound scheme), but alarm events with priority 1 will override my sound settings and always be loud and clear. Pretty good if you have the phone turned down etc. (It does mean you need to fine tune the ZM pretty well to avoid false events etc.)
I implemented pushover into the zmeventnotification.pl, like this.
on line 1044 i added this module ..its just at the end before the main part. it could be anyware ;O)
sub pushover{ LWP::UserAgent->new()->post( 'https://api.pushover.net/1/messages.json', [ 'token' => 'mysrecettoken', 'user' => 'mysrecetuser',
'priority' => '1',
'message' =>'ZM ALARM',
]);
}
And again here line 1094 .. tjis is where an event will trigger a notification of some sort.
if (checkEvents())
{
#also send notification with pushover
pushover();
I like it...I can now control the priority, have sound settings override mute, DND, and quite hours etc, and select my own sound scheme...precisely just like I want it ;O)
If people with those notifications apps, prefer them, it could take the load of your event server. It may we worth while to incorporate them into the available options inside zmeventnotification.pl. Just a thought.
Good work on the zmeventnotification script and app.
Cheers.
You are not getting the default sound even after changing $useCustomNotificationSound to 0 and restarting zmeventnotification?
no..I have selected 0..
my $useCustomNotificationSound = 0; # set to 0 for default sound
Does not make any differences. What should I expect...my alarm sound selected in android sound settings. Still getting the low blop.
I'm pretty sure I have updated everything...and even rebooted the server just to be sure. zmeventnotification.pl says my $app_version="0.7"; android app 1.0.7
I am getting your testing notification banners...keep up the good work ;O)
When re was referring to "those" notifications apps..i forgot to mention that i also tested notifymyandroid. It will do just the same, but i ended up with pushover...mainly because of their test period scheme.. pushover..free for a week. notifymyandroid 5 notifications pr. day. Impossible to test anything..when you dont even know when its reset, or get a notification that the original notification was withheld..just stupid.
go figure
I dont know if its important for this issue..but im getting quite a few errors kin ZM log like the ones below.
file:// is not found in servers list. /usr/share/zoneminder/includes/functions.php 111
The file is there and looks ok ??
Date/Time Component Server PID Level Message File Line 2016-02-09 18:40:35.881553 web_php 2565 INF Login successful for user "admin" /usr/share/zoneminder/includes/functions.php 59 2016-02-09 18:40:35.878010 web_php 2565 WAR file:// is not found in servers list. /usr/share/zoneminder/includes/functions.php 111 2016-02-09 18:40:11.443900 web_php 1612 INF Login successful for user "admin" /usr/share/zoneminder/includes/functions.php 59 2016-02-09 18:40:11.441872 web_php 1612 WAR file:// is not found in servers list. /usr/share/zoneminder/includes/functions.php 111 2016-02-09 18:40:09.152223 web_php 2356 INF Login successful for user "admin" /usr/share/zoneminder/includes/functions.php 59 2016-02-09 18:40:09.149499 web_php 2356 WAR file:// is not found in servers list. /usr/share/zoneminder/includes/functions.php 111
Those warnings are related to the new multi-server feature of ZM -- does not affect push
I'm still interested in debugging why you are not getting the default sound. If you have time please join #zoneminder IRC on freenode during the day - my handle is asker. I'd like to do a live debug session with you
Hi sorry I was not available, as have just seen your last post. I have a bit of a confession to make.
You have pushed some messages about updating the zoneminder, so I just check my version history, and am frankly a bit confused about which version I am actually running. For info I am running a brand new Debian 8 installation. There seems to be quite a lot of confusion about whether or not 1.29 will be available for Debian 8 ?
When checking in the Web interface it says.
Version: You are running the most recent version of ZoneMinder, v1.29.0.
But..when I installed, I Cloned from git (master) and installed more or less like described here http://sysadm.mielnet.pl/build-and-install-zoneminder-1-28-on-debian-8-jessie/
When I wanted to update, it did the same thing, and was surprised to see that the deb packets was the same name? My current installation and the new build cloned from GIT are both based binaries with this name: zoneminder_1.28.0+nmu1_amd64
Initial i did not get the API automatic, so I cloned the zoneminder version 1.2.9 and copied the API to my usr/share/zoneminder/api. The API works perfect, and i use it for my own alarm integration and obviously with your app.
https://localhost/zm/api/index.php are all in the green. see below Release Notes for CakePHP 2.5.0-RC1. Read the changelog Your version of PHP is 5.2.8 or higher. Your tmp directory is writable. The FileEngine is being used for core caching. To change the config edit APP/Config/core.php Your database configuration file is present. CakePHP is able to connect to the database.
I think I am really running a 1.28 + added 1.29 API
Is there a cli command to get the zoneminder "real" version. I have looked and can not find any.
No sure how to proceed...if i can update or change to a full version of 1.29 or if i can / should update the working setup I have right now.
Sorry if this got a bit out of ZMNinja scope..but above may be related to the issues I'm having. A similar situation is discussed here ..https://forums.zoneminder.com/viewtopic.php?t=23891
This seems to have been realized already ;O) https://forums.zoneminder.com/viewtopic.php?f=34&t=23949
Hi, this does not matter to push. The most important things are:
a) Make sure you are running 0.7 of the event server. I mean actually running with useCustomSound = 0 If customSound is 0, then it does not send a 'sound=blop' parameter to zmNinja. If you see your logs and its sending sound=blop, then something is wrong. When zmNinja does not get sound=blop from the event server it plays the default sound
To make sure you are actually running zmeventserver with custom sound set to 0, do a sudo zmdc.pl restart zmeventnotification.pl
after you update /usr/bin/zmeventserver.pl
b) Make sure you are running the latest version of zmNinja on android
Thanks ..Then I'm all good for push..except..i thought...what the heck..its just file naming issue..so i updated my zoneminder installation with the newest clone from GIT...and now my zm installation is broken. I can see my monitors in ZMninja..and it makes events on movement..but the monitor and event view does not have an image any more...I had to change file permissions to the cgi-bin/nph-zms folder from root to www-data
I have changed zms streaming server to /zm/cgi-bin/nph-zms under zm web interface options - paths..
I habv been trying to figure out what happen for a while now..suk..
I may have to role back to the old binaries..if i can.
you should read the install guide - you probably missed updating PATH_ZMS http://zoneminder.readthedocs.org/en/stable/installationguide/index.html
It was the first thing i did.. PATH_ZMS is set to /zm/cgi-bin/nph-zms just added teh /zm/ And the symlink in usr/share/zoneminder/cgi-bin points to usr/lib/cgi-bin prmissions set to www.data
Im totally lost..why can I see my cams in znminja..event are beeing created etc..but i can not view them or see the monitors.
All monitors are in the green..
I just updated from git..precisely as you linked guide says.
Got it up again -- pyy Turns out after a GIT update ..its a really good idear to run zmupdate.pl -f Then for for some reason..my PATH_ZMS must be set to /cgi-bin/nph-zms Oh well...only cost me half an evening..;O) The damnedest thing was it was allready set correctly and i changed it anyway.
That said...still getting the blob sound ..and I have double checked.. Here directly copied from usr/bin/zmeventnotification.pl
my $app_version="0.7";
my $useCustomNotificationSound = 0; # set to 0 for default sound
App updated as per google play.
getting the blob sound when app is running.
getting nothing when app is off.
Still happy with my pushover hack..it works really well thanks to your zmeventnotification.pl
But that is perhaps not for everyone...I can not be the only one with this issue.
Is there a way to check with another zm installation and zmeventnotification.pl implementation ..anyone?
;O)
zmNinja side: In zmNinja, enable debug logs Exit zmNinja start zmNinja Please send me the Debug logs. Please also mention Android version
ZM side: Please send me logs from zmeventnotification when you generate an event.
Dear Pliable
Debug logs enabled in zmninja
Running HTC m7 on _ViperOne 9.0.0-fix All_custom rom a android. Its an Android 5.0.2 sense 7.0 enhanced clone http://venomroms.com/htc-one-m7
rooted, x-off and newest firmware...android is all good.
Server
ruining a Debian 8 new install on a powerfull xeon 6 core 12GB ram dell precision t5500 (took the 2 CPU out for power saving reasons..12 cores would have been a bit much ;o)
Its normally running a VPN server and vpn gateway and a complicated firewall + zm..but for the logs i have deniabled all vpn, firwewall and ssl seceriyty..to simplify things, and avoid some strange firewall block etc.
I also copyed the zmeventnotification.pl, changed the few things needed...secrurty and sound = 0 and tokenfile, but no pushover hack.
see logs attached.
I got standard blob when running zmninja..nothing when the app was closed
no icon or notification banner on android..but the bell icon inside zmninja..on blob
on monitor montage..I also get red monitor name.
Hope it will help you in your debugging and development.
Keep up the good work..
let me know if you need more.
One question if you have time
If i wanted to include the monitor name that has an alarm..how would i modify the pushover module...to add this info and add it to the ZM ALARM string
sub pushover{ LWP::UserAgent->new()->post( 'https://api.pushover.net/1/messages.json', [ 'token' => 'yyyyyyy', 'user' => 'xxxxxx',
'priority' => '1',
'message' =>'ZM ALARM on -- monitor name here
----', ]); }
Requaero Those who search shall find
On 14 February 2016 at 00:27, Pliable Pixels notifications@github.com wrote:
zmNinja side: In zmNinja, enable debug logs Exit zmNinja start zmNinja Please send me the Debug logs. Please also mention Android version
ZM side: Please send me logs from zmeventnotification when you generate an event.
— Reply to this email directly or view it on GitHub https://github.com/pliablepixels/zmNinja/issues/135#issuecomment-183770926 .
#
#
#
#
#
#
#
#
#
use File::Basename; use Data::Dumper;
use strict; use bytes;
my $app_version="0.7";
#
#
use constant EVENT_NOTIFICATION_PORT=>9000; # port for Websockets connection
my $useSecure = 0; # make this 0 if you don't want SSL
use constant SSL_CERT_FILE=>'/etc/apache2/ssl/zoneminder.crt'; # Change these to your certs/keys use constant SSL_KEY_FILE=>'/etc/apache2/ssl/zoneminder.key';
my $usePushProxy = 1; # set this to 1 to use a remote push proxy for APNS that I have set up for zmNinja users
my $usePushAPNSDirect = 0; # set this to 1 if you have an APNS SSL certificate/key pair
# account
my $pushProxyURL = 'https://pliablepixels.ddns.net:8801'; # This is my proxy URL. Don't change it unless you are hosting your on APNS AS
my $useCustomNotificationSound = 0; # set to 0 for default sound
use constant PUSH_TOKEN_FILE=>'/etc/zmninja/tokens.txt'; # MAKE SURE THIS DIRECTORY HAS WWW-DATA PERMISSIONS
my %ssl_push_opts = ( ssl_opts=>{verify_hostname => 0,SSL_verify_mode => 0,SSL_verifycn_scheme => 'none'} );
my $isSandbox = 1; # 1 or 0 depending on your APNS certificate
use constant APNS_CERT_FILE=>'/etc/private/apns-dev-cert.pem'; # only used if usePushAPNSDirect is enabled use constant APNS_KEY_FILE=>'/etc/private/apns-dev-key.pem'; # only used if usePushAPNSDirect is enabled
use constant APNS_FEEDBACK_CHECK_INTERVAL => 3600; # only used if usePushAPNSDirect is enabled
use constant PUSH_CHECK_REACH_INTERVAL => 3600; # time in seconds to do a reachability test with push proxt use constant SLEEP_DELAY=>5; # duration in seconds after which we will check for new events use constant MONITOR_RELOAD_INTERVAL => 300; use constant WEBSOCKET_AUTH_DELAY => 20; # max seconds by which authentication must be done
use constant PUSHPROXY_APP_NAME => 'zmninjapro'; use constant PUSHPROXY_APP_ID => '3905d86d1f2922ed8f77583058471d70';
use constant PENDING_WEBSOCKET => '1'; use constant INVALID_WEBSOCKET => '-1'; use constant INVALID_APNS => '-2'; use constant VALID_WEBSOCKET => '0';
my $alarmEventId = 1; # tags the event id along with the alarm - useful for correlation
if (!try_use ("Net::WebSocket::Server")) {Fatal ("Net::WebSocket::Server missing");exit (-1);} if (!try_use ("IO::Socket::SSL")) {Fatal ("IO::Socket::SSL missing");exit (-1);} if (!try_use ("Crypt::MySQL qw(password password41)")) {Fatal ("Crypt::MySQL missing");exit (-1);}
if (!try_use ("JSON")) { if (!try_use ("JSON::XS")) { Fatal ("JSON or JSON::XS missing");exit (-1);} }
if ($usePushProxy) { if ($usePushAPNSDirect) { $usePushAPNSDirect = 0; Info ("Disabling direct push as push proxy is enabled"); } if (!try_use ("LWP::UserAgent") || !try_use ("URI::URL") || !try_use("LWP::Protocol::https")) { Error ("Disabling PushProxy. PushProxy mode needs LWP::Protocol::https, LWP::UserAgent and URI::URL perl packages installed"); $usePushProxy = 0; } else { Info ("Push enabled via PushProxy"); }
} else { Info ("Push Proxy disabled"); }
if ($usePushAPNSDirect ) { if (!try_use ("Net::APNS::Persistent") || !try_use ("Net::APNS::Feedback")) { Error ("Net::APNS::Feedback and/or Net::APNS::Persistent not present. Disabling direct APNS support"); $usePushAPNSDirect = 0;
}
else
{
Info ("direct APNS support loaded");
}
} else { Info ("direct APNS disabled"); }
#
#
use lib '/usr/local/lib/x86_64-linux-gnu/perl5'; use ZoneMinder; use POSIX; use DBI;
$| = 1;
$ENV{PATH} = '/bin:/usr/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
sub Usage { print( "This daemon is not meant to be invoked from command line\n"); exit( -1 ); }
logInit(); logSetSignal();
my $dbh = zmDbConnect(); my %monitors; my $monitor_reload_time = 0; my $apns_feedback_time = 0; my $proxy_reach_time=0; my $wss; my @events=(); my @active_connections=(); my $alarm_header="";
if ($usePushAPNSDirect || $usePushProxy) { my $dir = dirname(PUSH_TOKEN_FILE); if ( ! -d $dir) {
Info ("Creating $dir to store APNS tokens");
mkdir $dir;
}
}
Info( "Event Notification daemon v $app_version starting\n" ); loadTokens(); initSocketServer(); Info( "Event Notification daemon exiting\n" ); exit();
sub try_use { my $module = shift; eval("use $module"); return($@ ? 0:1); }
sub checkEvents() {
#foreach (@active_connections)
#{
#print " IP:".$_->{conn}->ip().":".$_->{conn}->port()."Token:".$_->{token}."\n";
#}
my $eventFound = 0;
if ( (time() - $monitor_reload_time) > MONITOR_RELOAD_INTERVAL )
{
my $len = scalar @active_connections;
Info ("Total event client connections: ".$len."\n");
Info ("Reloading Monitors...\n");
foreach my $monitor (values(%monitors))
{
zmMemInvalidate( $monitor );
}
loadMonitors();
}
@events = ();
$alarm_header = "";
foreach my $monitor ( values(%monitors) )
{
my ( $state, $last_event )
= zmMemRead( $monitor,
[ "shared_data:state",
"shared_data:last_event"
]
);
if ($state == STATE_ALARM || $state == STATE_ALERT)
{
if ( !defined($monitor->{LastEvent})
|| ($last_event != $monitor->{LastEvent}))
{
Info( "New event $last_event reported for ".$monitor->{Name}."\n");
$monitor->{LastState} = $state;
$monitor->{LastEvent} = $last_event;
my $name = $monitor->{Name};
my $mid = $monitor->{Id};
my $eid = $last_event;
push @events, {Name => $name, MonitorId => $mid, EventId => $last_event};
$alarm_header = "Alarms: " if (!$alarm_header);
$alarm_header = $alarm_header . $name ;
$alarm_header = $alarm_header . " (".$last_event.") " if ($alarmEventId);
$alarm_header = $alarm_header . "," ;
$eventFound = 1;
}
}
}
chop($alarm_header) if ($alarm_header);
return ($eventFound);
}
sub loadMonitors { Info( "Loading monitors\n" ); $monitor_reload_time = time();
my %new_monitors = ();
my $sql = "SELECT * FROM Monitors
WHERE find_in_set( Function, 'Modect,Mocord,Nodect' )"
;
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute()
or Fatal( "Can't execute: ".$sth->errstr() );
while( my $monitor = $sth->fetchrow_hashref() )
{
next if ( !zmMemVerify( $monitor ) ); # Check shared memory ok
if ( defined($monitors{$monitor->{Id}}->{LastState}) )
{
$monitor->{LastState} = $monitors{$monitor->{Id}}->{LastState};
}
else
{
$monitor->{LastState} = zmGetMonitorState( $monitor );
}
if ( defined($monitors{$monitor->{Id}}->{LastEvent}) )
{
$monitor->{LastEvent} = $monitors{$monitor->{Id}}->{LastEvent};
}
else
{
$monitor->{LastEvent} = zmGetLastEvent( $monitor );
}
$new_monitors{$monitor->{Id}} = $monitor;
}
%monitors = %new_monitors;
}
sub testProxyURL { if ((time() - $proxy_reach_time) > PUSH_CHECK_REACH_INTERVAL) { Info ("Checking $pushProxyURL reachability..."); my $ua = LWP::UserAgent->new(%ssl_push_opts); $ua->timeout(10); $ua->env_proxy; my $response = $ua->get($pushProxyURL); if ($response->is_success) { Info ("PushProxy $pushProxyURL is reachable.");
}
else
{
Error ($response->status_line);
Error ("PushProxy $pushProxyURL is NOT reachable. Notifications will not work. Please reach out to the proxy owner if this error persists");
}
$proxy_reach_time = time();
}
}
sub validateZM { my ($u,$p) = @_; return 0 if ( $u eq "" || $p eq ""); my $sql = 'select Password from Users where Username=?'; my $sth = $dbh->prepare_cached($sql) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $u ) or Fatal( "Can't execute: ".$sth->errstr() ); if (my ($state) = $sth->fetchrow_hashref()) { my $encryptedPassword = password41($p); $sth->finish(); return $state->{Password} eq $encryptedPassword ? 1:0; } else { $sth->finish(); return 0; }
}
sub registerOverPushProxy { my ($token) = shift; my ($platform) = shift; my $uri = $pushProxyURL."/api/v2/tokens"; my $json = '{"device":"'.$platform.'", "token":"'.$token.'", "channel":"default"}'; my $req = HTTP::Request->new ('POST', $uri); $req->header( 'Content-Type' => 'application/json', 'X-AN-APP-NAME'=> PUSHPROXY_APP_NAME, 'X-AN-APP-KEY'=> PUSHPROXY_APP_ID ); $req->content($json); my $lwp = LWP::UserAgent->new(%ssl_push_opts); my $res = $lwp->request( $req ); if ($res->is_success) { Info ("Pushproxy registration success ".$res->content); } else { Warning("Push Proxy Token registration Error:".$res->status_line); }
}
sub sendOverPushProxy {
my ($obj, $header, $str) = @_;
$obj->{badge}++;
my $uri = $pushProxyURL."/api/v2/push";
my $json;
# Not passing full JSON object - so that payload is limited for now
if ($obj->{platform} eq "ios")
{
if ($useCustomNotificationSound)
{
$json = '{"device":"'.$obj->{platform}.'", "token":"'.$obj->{token}.'", "alert":"'.$header.'", "sound":"blop.caf", "badge":"'.$obj->{badge}.'"}';
}
else
{
$json = '{"device":"'.$obj->{platform}.'", "token":"'.$obj->{token}.'", "alert":"'.$header.'", "sound":"true", "badge":"'.$obj->{badge}.'"}';
}
}
else
{
if ($useCustomNotificationSound)
{
$json = '{"device":"'.$obj->{platform}.'", "token":"'.$obj->{token}.'", "sound":"blop", "alert":"'.$header.'"}';
}
else
{
$json = '{"device":"'.$obj->{platform}.'", "token":"'.$obj->{token}.'", "alert":"'.$header.'"}';
}
}
#print "Sending:$json\n";
my $req = HTTP::Request->new ('POST', $uri);
$req->header( 'Content-Type' => 'application/json', 'X-AN-APP-NAME'=> PUSHPROXY_APP_NAME, 'X-AN-APP-KEY'=> PUSHPROXY_APP_ID
);
$req->content($json);
my $lwp = LWP::UserAgent->new(%ssl_push_opts);
my $res = $lwp->request( $req );
if ($res->is_success)
{
Info ("Pushproxy push message success ".$res->content);
}
else
{
Info("Push Proxy push message Error:".$res->status_line);
}
}
sub sendOverAPNS { if (!$usePushAPNSDirect) { Info ("Rejecting APNS request as daemon has APNS disabled"); return; }
my ($obj, $header, $str) = @_; my (%hash) = %{$str};
my $apns = Net::APNS::Persistent->new({
sandbox => $isSandbox,
cert => APNS_CERT_FILE,
key => APNS_KEY_FILE
});
$obj->{badge}++; $apns->queue_notification( $obj->{token}, { aps => { alert => $header, sound => 'default', badge => $obj->{badge}, }, alarm_details => \%hash });
$apns->send_queue; $apns->disconnect;
}
sub apnsFeedbackCheck {
if ((time() - $apns_feedback_time) > APNS_FEEDBACK_CHECK_INTERVAL)
{
if ($usePushProxy)
{
Info ("Not checking APNS feedback in PushProxy Mode");
return;
}
if (!$usePushAPNSDirect)
{
Info ("Rejecting APNS Feedback request as daemon has APNS disabled");
return;
}
Info ("Checking APNS Feedback\n");
$apns_feedback_time = time();
my $apnsfb = Net::APNS::Feedback->new({
sandbox => $isSandbox,
cert => APNS_CERT_FILE,
key => APNS_KEY_FILE
});
my @feedback = $apnsfb->retrieve_feedback;
foreach (@feedback[0]->[0])
{
my $delete_token = $_->{token};
if ($delete_token != "")
{
deleteToken($delete_token);
foreach(@active_connections)
{
if ($_->{token} eq $delete_token)
{
$_->{pending} = INVALID_APNS;
Info ("Marking entry as invalid apns token: ". $delete_token."\n");
}
}
}
}
}
}
sub checkConnection { foreach (@activeconnections) { my $curtime = time(); if ($->{pending} == PENDING_WEBSOCKET) {
if ($curtime - $_->{time} > WEBSOCKET_AUTH_DELAY)
{
# What happens if auth is not provided but device token is registered?
# It may still be a bogus token, so don't risk keeping connection stored
if (exists $_->{conn})
{
my $conn = $_->{conn};
Info ("Rejecting ".$conn->ip()." - authentication timeout");
$_->{pending} = INVALID_WEBSOCKET;
my $str = encode_json({event => 'auth', type=>'',status=>'Fail', reason => 'NOAUTH'});
eval {$_->{conn}->send_utf8($str);};
$_->{conn}->disconnect();
}
}
}
}
@active_connections = grep { $_->{pending} != INVALID_WEBSOCKET } @active_connections;
if ($usePushAPNSDirect || $usePushProxy)
{
@active_connections = grep { $_->{pending} != INVALID_APNS } @active_connections;
}
}
sub checkMessage { my ($conn, $msg) = @_;
my $json_string;
eval {$json_string = decode_json($msg);};
if ($@)
{
my $str = encode_json({event=> 'malformed', type=>'', status=>'Fail', reason=>'BADJSON'});
eval {$conn->send_utf8($str);};
return;
}
#print "Message:$msg\n";
# This event type is when a command related to push notification is received
if (($json_string->{'event'} eq "push") && !$usePushAPNSDirect && !$usePushProxy)
{
my $str = encode_json({event=>'push', type=>'',status=>'Fail', reason => 'PUSHDISABLED'});
eval {$conn->send_utf8($str);};
return;
}
#-----------------------------------------------------------------------------------
# "push" event processing
#-----------------------------------------------------------------------------------
elsif (($json_string->{'event'} eq "push") && ($usePushAPNSDirect || $usePushProxy))
{
# sets the unread event count of events for a specific connection
# the server keeps a tab of # of events it pushes out per connection
# but won't know when the client has read them, so the client call tell the server
# using this message
if ($json_string->{'data'}->{'type'} eq "badge")
{
foreach (@active_connections)
{
if ((exists $_->{conn}) && ($_->{conn}->ip() eq $conn->ip()) &&
($_->{conn}->port() eq $conn->port()))
{
#print "Badge match, setting to 0\n";
$_->{badge} = $json_string->{'data'}->{'badge'};
}
}
}
# This sub type is when a device token is registered
if ($json_string->{'data'}->{'type'} eq "token")
{
# a token must have a platform otherwise I don't know whether to use APNS or GCM
if (!$json_string->{'data'}->{'platform'})
{
my $str = encode_json({event=>'push', type=>'token',status=>'Fail', reason => 'MISSINGPLATFORM'});
eval {$conn->send_utf8($str);};
return;
}
foreach (@active_connections)
{
# this token already exists
if ($_->{token} eq $json_string->{'data'}->{'token'})
{
# if the token doesn't belong to the same connection
# then we have two connections owning the same token
# so we need to delete the old one. This can happen when you load
# the token from the persistent file and there is no connection
# and then the client is loaded
if ( (!exists $_->{conn}) || ($_->{conn}->ip() ne $conn->ip()
&& $_->{conn}->port() ne $conn->port()))
{
$_->{pending} = INVALID_APNS;
Info ("Duplicate token found, marking for deletion");
}
else # token matches and connection matches, so it may be an update
{
$_->{token} = $json_string->{'data'}->{'token'};
$_->{platform} = $json_string->{'data'}->{'platform'};
$_->{monlist} = "-1";
$_->{intlist} = "-1";
$_->{pushstate} = $json_string->{'data'}->{'state'};
Info ("Storing token ...".substr($_->{token},-10).",monlist:".$_->{monlist}.",intlist:".$_->{intlist}.",pushstate:".$_->{pushstate}."\n");
my ($emonlist,$eintlist) = saveTokens($_->{token}, $_->{monlist}, $_->{intlist}, $_->{platform}, $_->{pushstate});
$_->{monlist} = $emonlist;
$_->{intlist} = $eintlist;
}
}
# The connection matches but the token does not
# this can happen if this is the first token registration after push notification registration
# response is received
elsif ( (exists $_->{conn}) && ($_->{conn}->ip() eq $conn->ip()) &&
($_->{conn}->port() eq $conn->port()))
{
$_->{token} = $json_string->{'data'}->{'token'};
$_->{platform} = $json_string->{'data'}->{'platform'};
$_->{monlist} = "-1";
$_->{intlist} = "-1";
$_->{pushstate} = $json_string->{'data'}->{'state'};
Info ("Storing token ...".substr($_->{token},-10).",monlist:".$_->{monlist}.",intlist:".$_->{intlist}.",pushstate:".$_->{pushstate}."\n");
my ($emonlist,$eintlist) = saveTokens($_->{token}, $_->{monlist}, $_->{intlist}, $_->{platform}, $_->{pushstate});
$_->{monlist} = $emonlist;
$_->{intlist} = $eintlist;
}
}
}
} # event = push
#-----------------------------------------------------------------------------------
# "control" event processing
#-----------------------------------------------------------------------------------
elsif (($json_string->{'event'} eq "control") )
{
if ($json_string->{'data'}->{'type'} eq "filter")
{
if (!$json_string->{'data'}->{'monlist'})
{
my $str = encode_json({event=>'control', type=>'filter',status=>'Fail', reason => 'MISSINGMONITORLIST'});
eval {$conn->send_utf8($str);};
return;
}
if (!$json_string->{'data'}->{'intlist'})
{
my $str = encode_json({event=>'control', type=>'filter',status=>'Fail', reason => 'MISSINGINTERVALLIST'});
eval {$conn->send_utf8($str);};
return;
}
my $monlist = $json_string->{'data'}->{'monlist'};
my $intlist = $json_string->{'data'}->{'intlist'};
#print ("CONTROL GOT: $monlist and $intlist\n");
foreach (@active_connections)
{
if ((exists $_->{conn}) && ($_->{conn}->ip() eq $conn->ip()) &&
($_->{conn}->port() eq $conn->port()))
{
$_->{monlist} = $monlist;
$_->{intlist} = $intlist;
Info ("Contrl: Storing token ...".substr($_->{token},-10).",monlist:".$_->{monlist}.",intlist:".$_->{intlist}.",pushstate:".$_->{pushstate}."\n");
saveTokens($_->{token}, $_->{monlist}, $_->{intlist}, $_->{platform}, $_->{pushstate});
}
}
}
if ($json_string->{'data'}->{'type'} eq "version")
{
foreach (@active_connections)
{
if ((exists $_->{conn}) && ($_->{conn}->ip() eq $conn->ip()) &&
($_->{conn}->port() eq $conn->port()))
{
my $str = encode_json({event=>'control',type=>'version', status=>'Success', reason => '', version => $app_version});
eval {$_->{conn}->send_utf8($str);};
}
}
}
} # event = control
#-----------------------------------------------------------------------------------
# "auth" event processing
#-----------------------------------------------------------------------------------
# This event type is when a command related to authorization is sent
elsif ($json_string->{'event'} eq "auth")
{
my $uname = $json_string->{'data'}->{'user'};
my $pwd = $json_string->{'data'}->{'password'};
return if ($uname eq "" || $pwd eq "");
foreach (@active_connections)
{
if ( (exists $_->{conn}) &&
($_->{conn}->ip() eq $conn->ip()) &&
($_->{conn}->port() eq $conn->port()) &&
($_->{pending}==PENDING_WEBSOCKET))
{
if (!validateZM($uname,$pwd))
{
# bad username or password, so reject and mark for deletion
my $str = encode_json({event=>'auth', type=>'', status=>'Fail', reason => 'BADAUTH'});
eval {$_->{conn}->send_utf8($str);};
Info("Bad authentication provided by ".$_->{conn}->ip());
$_->{pending}=INVALID_WEBSOCKET;
}
else
{
# all good, connection auth was valid
$_->{pending}=VALID_WEBSOCKET;
$_->{token}='';
my $str = encode_json({event=>'auth', type=>'', status=>'Success', reason => '', version => $app_version});
eval {$_->{conn}->send_utf8($str);};
Info("Correct authentication provided by ".$_->{conn}->ip());
}
}
}
} # event = auth
else
{
my $str = encode_json({event=>$json_string->{'event'},type=>'', status=>'Fail', reason => 'NOTSUPPORTED'});
eval {$_->{conn}->send_utf8($str);};
}
}
sub loadTokens { return if (!$usePushAPNSDirect && !$usePushProxy); if ( ! -f PUSH_TOKEN_FILE) { open (my $foh, '>', PUSH_TOKEN_FILE); Info ("Creating ".PUSH_TOKEN_FILE); print $foh ""; close ($foh); }
open (my $fh, '<', PUSH_TOKEN_FILE);
chomp( my @lines = <$fh>);
close ($fh);
my @uniquetokens = uniq(@lines);
open ($fh, '>', PUSH_TOKEN_FILE);
# This makes sure we rewrite the file with
# unique tokens
foreach(@uniquetokens)
{
next if ($_ eq "");
print $fh "$_\n";
my ($token, $monlist, $intlist, $platform, $pushstate) = split (":",$_);
#print "load: PUSHING $row\n";
push @active_connections, {
token => $token,
pending => VALID_WEBSOCKET,
time=>time(),
badge => 0,
monlist => $monlist,
intlist => $intlist,
last_sent=>{},
platform => $platform,
pushstate => $pushstate
};
}
close ($fh);
}
sub deleteToken { my $dtoken = shift; return if (!$usePushAPNSDirect && !$usePushProxy); return if ( ! -f PUSH_TOKEN_FILE);
open (my $fh, '<', PUSH_TOKEN_FILE);
chomp( my @lines = <$fh>);
close ($fh);
my @uniquetokens = uniq(@lines);
open ($fh, '>', PUSH_TOKEN_FILE);
foreach(@uniquetokens)
{
my ($token, $monlist, $intlist, $platform, $pushstate) = split (":",$_);
next if ($_ eq "" || $token eq $dtoken);
print $fh "$_\n";
#print "delete: $row\n";
push @active_connections, {
token => $token,
pending => VALID_WEBSOCKET,
time=>time(),
badge => 0,
monlist => $monlist,
intlist => $intlist,
last_sent=>{},
platform => $platform,
pushstate => $pushstate
};
}
close ($fh);
}
sub saveTokens { return if (!$usePushAPNSDirect && !$usePushProxy); my $stoken = shift; return if ($stoken eq ""); my $smonlist = shift; my $sintlist = shift; my $splatform = shift; my $spushstate = shift; return if ($stoken eq ""); open (my $fh, '<', PUSH_TOKEN_FILE) || Fatal ("Cannot open for read".PUSH_TOKEN_FILE); chomp( my @lines = <$fh>); close ($fh); my @uniquetokens = uniq(@lines); my $found = 0; open (my $fh, '>', PUSH_TOKEN_FILE) || Fatal ("Cannot open for write ".PUSH_TOKENFILE); foreach (@uniquetokens) { next if ($ eq ""); my ($token, $monlist, $intlist, $platform, $pushstate) = split (":",$_); if ($token eq $stoken) { $smonlist = $monlist if ($smonlist eq "-1"); $sintlist = $intlist if ($sintlist eq "-1"); $spushstate = $pushstate if ($spushstate eq ""); print $fh "$stoken:$smonlist:$sintlist:$splatform:$spushstate\n"; $found = 1; } else { $pushstate="enabled" if ($pushstate=""); print $fh "$token:$monlist:$intlist:$platform:$pushstate\n"; }
}
$smonlist = "" if ($smonlist eq "-1");
$sintlist = "" if ($sintlist eq "-1");
print $fh "$stoken:$smonlist:$sintlist:$splatform:$spushstate\n" if (!$found);
close ($fh);
registerOverPushProxy($stoken,$splatform) if ($usePushProxy);
#print "Saved Token $token to file\n";
return ($smonlist, $sintlist);
}
sub uniq { my %seen; my @array = reverse @; # we want the latest my @farray=(); foreach (@array) { my ($token,$monlist,$intlist,$platform, $pushstate) = split (":",$);
if (! $seen{$token}++ )
{
push @farray, "$token:$monlist:$intlist:$platform:$pushstate";
}
}
return @farray;
}
sub getInterval { my $intlist = shift; my $monlist = shift; my $mid = shift;
#print ("getInterval:MID:$mid INT:$intlist AND MON:$monlist\n");
my @ints = split (',',$intlist);
my @mids = split (',',$monlist);
my $idx = -1;
foreach (@mids)
{
$idx++;
#print ("Comparing $mid with $_\n");
if ($mid eq $_)
{
last;
}
}
#print ("RETURNING index:$idx with Value:".$ints[$idx]."\n");
return $ints[$idx];
}
sub isInList { my $monlist = shift; my $mid = shift;
my @mids = split (',',$monlist);
my $found = 0;
foreach (@mids)
{
if ($mid eq $_)
{
$found = 1;
last;
}
}
return $found;
}
sub getIdentity { my $obj=shift; my $identity=""; if (exists $obj->{conn} ) { $identity = $obj->{conn}->ip().":".$obj->{conn}->port(); } if ($obj->{token}) { $identity=$identity." token ending in:...". substr($obj->{token},-10); } $identity="(unknown)" if (!$identity); return $identity; }
sub initSocketServer { checkEvents(); testProxyURL() if ($usePushProxy);
my $ssl_server;
if ($useSecure)
{
$ssl_server = IO::Socket::SSL->new(
Listen => 10,
LocalPort => EVENT_NOTIFICATION_PORT,
Proto => 'tcp',
Reuse => 1,
SSL_cert_file => SSL_CERT_FILE,
SSL_key_file => SSL_KEY_FILE
) or die "failed to listen: $!";
Info ("Secure WS(WSS) is enabled...");
}
else
{
Info ("Secure WS is disabled...");
}
Info ("Web Socket Event Server listening on port ".EVENT_NOTIFICATION_PORT."\n");
$wss = Net::WebSocket::Server->new(
listen => $useSecure ? $ssl_server : EVENT_NOTIFICATION_PORT,
tick_period => SLEEP_DELAY,
on_tick => sub {
checkConnection();
apnsFeedbackCheck() if ($usePushAPNSDirect);
testProxyURL() if ($usePushProxy);
my $ac = scalar @active_connections;
if (checkEvents())
{
Info ("Broadcasting new events to all $ac websocket clients\n");
my ($serv) = @_;
my $i = 0;
foreach (@active_connections)
{
# Let's see if this connection is interested in this alarm
my $monlist = $_->{monlist};
my $intlist = $_->{intlist};
my $last_sent = $_->{last_sent};
my $obj = $_;
my $connid = getIdentity($obj);
Info ("Checking alarm rules for $connid");
# we need to create a per connection array which will be
# a subset of main events with the ones that are not in its
# monlist left out
my @localevents = ();
foreach (@events)
{
if ($monlist eq "" || isInList($monlist, $_->{MonitorId} ) )
{
my $mint = getInterval($intlist, $monlist, $_->{MonitorId});
my $elapsed;
if ($last_sent->{$_->{MonitorId}})
{
$elapsed = time() - $last_sent->{$_->{MonitorId}};
if ($elapsed >= $mint)
{
Info("Monitor ".$_->{MonitorId}." event: sending this out as $elapsed is >= interval of $mint");
push (@localevents, $_);
$last_sent->{$_->{MonitorId}} = time();
}
else
{
Info("Monitor ".$_->{MonitorId}." event: NOT sending this out as $elapsed is less than interval of $mint");
}
}
else
{
# This means we have no record of sending any event to this monitor
$last_sent->{$_->{MonitorId}} = time();
Info("Monitor ".$_->{MonitorId}." event: last time not found, so sending");
push (@localevents, $_);
}
}
}
# if this array is empty that means none of the alarms
# were generated from a monitor it is interested in
next if (scalar @localevents == 0);
my $str = encode_json({event => 'alarm', type=>'', status=>'Success', events => \@localevents});
my $sup_str = encode_json({event => 'alarm', type=>'', status=>'Success', supplementary=>'true', events => \@localevents});
my %hash_str = (event => 'alarm', status=>'Success', events => \@localevents);
$i++;
# if there is APNS send it over APNS
# if not, send it over Websockets
# also disabled is a special state which means its registered over push
# but it still wants messages over websockets - zmNinja sets this
# when websockets override is enabled
if (($_->{token} ne "") && ($_->{pushstate} ne "disabled" ))
{
if ($usePushProxy)
{
Info ("Sending notification over PushProxy");
sendOverPushProxy($_,$alarm_header, $str) ;
}
elsif ($usePushAPNSDirect)
{
Info ("Sending notification directly via APNS");
sendOverAPNS($_,$alarm_header, \%hash_str) ;
}
# send supplementary event data over websocket
if ($_->{pending} == VALID_WEBSOCKET)
{
if (exists $_->{conn})
{
Info ($_->{conn}->ip()."-sending supplementary data over websockets\n");
eval {$_->{conn}->send_utf8($sup_str);};
if ($@)
{
$_->{pending} = INVALID_WEBSOCKET;
}
}
}
}
# if there is a websocket send it over websockets
elsif ($_->{pending} == VALID_WEBSOCKET)
{
if (exists $_->{conn})
{
Info ($_->{conn}->ip()."-sending over websockets\n");
eval {$_->{conn}->send_utf8($str);};
if ($@)
{
$_->{pending} = INVALID_WEBSOCKET;
}
}
}
}
}
},
# called when a new connection comes in
on_connect => sub {
my ($serv, $conn) = @_;
my ($len) = scalar @active_connections;
Info ("got a websocket connection from ".$conn->ip()." (". $len.") active connections");
$conn->on(
utf8 => sub {
my ($conn, $msg) = @_;
checkMessage($conn, $msg);
},
handshake => sub {
my ($conn, $handshake) = @_;
Info ("Websockets: New Connection Handshake requested from ".$conn->ip().":".$conn->port()." state=pending auth");
my $connect_time = time();
push @active_connections, {conn => $conn,
pending => PENDING_WEBSOCKET,
time=>$connect_time,
monlist => "",
intlist => "",
last_sent=>{},
platform => "websocket",
pushstate => '',
badge => 0};
},
disconnect => sub
{
my ($conn, $code, $reason) = @_;
Info ("Websocket remotely disconnected from ".$conn->ip());
foreach (@active_connections)
{
if ((exists $_->{conn}) && ($_->{conn}->ip() eq $conn->ip()) &&
($_->{conn}->port() eq $conn->port()))
{
# mark this for deletion only if device token
# not present
if ( $_->{token} eq '')
{
$_->{pending}=INVALID_WEBSOCKET;
Info( "Marking ".$conn->ip()." for deletion as websocket closed remotely\n");
}
else
{
Info( "NOT Marking ".$conn->ip()." for deletion as token ".$_->{token}." active\n");
}
}
}
},
);
}
)->start;
}
Date/Time
Component Server PID Level Message File Line
2016-02-19 09:57:28.939218 zma_m1 2523 INF Indkorsel: 11000 - Analysing at 5.52 fps zm_monitor.cpp 1287
2016-02-19 09:57:20.406754 zmc_m4 2546 INF Garage: 45000 - Capturing at 22.22 fps zm_monitor.cpp 3131
2016-02-19 09:57:14.835598 zma_m4 2551 INF Garage: 11000 - Analysing at 5.43 fps zm_monitor.cpp 1287
2016-02-19 09:57:07.038349 zmc_m1 2519 INF Indkorsel: 43000 - Capturing at 21.28 fps zm_monitor.cpp 3131
2016-02-19 09:56:52.731834 zmc_m3 5572 INF Terrasse: 9000 - Capturing at 18.52 fps zm_monitor.cpp 3131
2016-02-19 09:56:51.672354 web_js 2306 ERR Export request failed: 500 / Internal Server Error - exportFail() ?view=log
2016-02-19 09:56:44.126664 web_js 2746 ERR Export request failed: 500 / Internal Server Error - exportFail() ?view=log
2016-02-19 09:56:35.468823 zmc_m4 2546 INF Garage: 44000 - Capturing at 22.22 fps zm_monitor.cpp 3131
2016-02-19 09:56:20.552037 zmc_m1 2519 INF Indkorsel: 42000 - Capturing at 21.74 fps zm_monitor.cpp 3131
2016-02-19 09:55:58.650479 zmc_m3 5572 INF Terrasse: 8000 - Capturing at 18.18 fps zm_monitor.cpp 3131
2016-02-19 09:55:53.316918 zma_m3 2535 INF Terrasse: 11000 - Analysing at 5.26 fps zm_monitor.cpp 1287
2016-02-19 09:55:50.705142 zmc_m4 2546 INF Garage: 43000 - Capturing at 22.22 fps zm_monitor.cpp 3131
2016-02-19 09:55:34.230602 zmc_m1 2519 INF Indkorsel: 41000 - Capturing at 21.28 fps zm_monitor.cpp 3131
2016-02-19 09:55:08.066938 zma_m3 2535 INF Terrasse: 10742 - Closing event 241, alarm end zm_monitor.cpp 1707
2016-02-19 09:55:08.065944 zma_m3 2535 INF Terrasse: 10742 - Left alarm state (241) - 119(44) images zm_monitor.cpp 1702
2016-02-19 09:55:05.457454 zmc_m4 2546 INF Garage: 42000 - Capturing at 21.74 fps zm_monitor.cpp 3131
2016-02-19 09:55:03.230569 zmc_m3 5572 INF Terrasse: 7000 - Capturing at 17.54 fps zm_monitor.cpp 3131
2016-02-19 09:54:58.530678 zma_m3 2535 INF Terrasse: 10692 - Gone into alert state zm_monitor.cpp 1695
2016-02-19 09:54:50.296660 zmeventnotification 5723 INF 85.83.32.41-sending over websockets zmeventnotification.pl
2016-02-19 09:54:50.295950 zmeventnotification 5723 INF Monitor 3 event: last time not found, so sending zmeventnotification.pl
2016-02-19 09:54:50.295230 zmeventnotification 5723 INF Checking alarm rules for 85.83.32.41:54882 token ending in:...etePKHHtOi zmeventnotification.pl
2016-02-19 09:54:50.293980 zmeventnotification 5723 INF Broadcasting new events to all 1 websocket clients zmeventnotification.pl
2016-02-19 09:54:50.292810 zmeventnotification 5723 INF New event 241 reported for Terrasse zmeventnotification.pl
2016-02-19 09:54:49.792770 zma_m3 2535 INF Terrasse: 10657 - Opening new event 241, alarm start zm_monitor.cpp 1651
2016-02-19 09:54:49.791151 zma_m3 2535 INF Terrasse: 10657 - Gone into alarm state zm_monitor.cpp 1605
2016-02-19 09:54:47.921944 zma_m3 2535 INF Terrasse: 10648 - Gone into prealarm state zm_monitor.cpp 1680
2016-02-19 09:54:47.921293 zmc_m1 2519 INF Indkorsel: 40000 - Capturing at 21.74 fps zm_monitor.cpp 3131
2016-02-19 09:54:43.706839 zma_m3 2535 INF Terrasse: 10624 - Closing event 240, alarm end zm_monitor.cpp 1707
2016-02-19 09:54:43.705903 zma_m3 2535 INF Terrasse: 10624 - Left alarm state (240) - 153(76) images zm_monitor.cpp 1702
2016-02-19 09:54:41.103170 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl
2016-02-19 09:54:40.144580 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl
2016-02-19 09:54:40.142400 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl
2016-02-19 09:54:39.472330 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl
2016-02-19 09:54:39.470540 zmeventnotification 5723 INF Correct authentication provided by 85.83.32.41 zmeventnotification.pl
2016-02-19 09:54:39.314820 zmeventnotification 5723 INF Websockets: New Connection Handshake requested from 85.83.32.41:54882 state=pending auth zmeventnotification.pl
2016-02-19 09:54:39.312760 zmeventnotification 5723 INF got a websocket connection from 85.83.32.41 (0) active connections zmeventnotification.pl
2016-02-19 09:54:38.751025 web_php 3247 INF Login successful for user "admin" /usr/share/zoneminder/includes/functions.php 59
2016-02-19 09:54:34.232045 zma_m3 2535 INF Terrasse: 10574 - Gone into alert state zm_monitor.cpp 1695
2016-02-19 09:54:27.020207 zma_m1 2523 INF Indkorsel: 10000 - Analysing at 5.46 fps zm_monitor.cpp 1287
2016-02-19 09:54:24.564552 zma_m3 2535 INF Terrasse: 10532 - Gone back into alarm state zm_monitor.cpp 1686
2016-02-19 09:54:24.142570 zma_m3 2535 INF Terrasse: 10530 - Gone into alert state zm_monitor.cpp 1695
2016-02-19 09:54:20.297200 zmeventnotification 5723 INF 0.0.0.0-sending over websockets zmeventnotification.pl
2016-02-19 09:54:20.296610 zmeventnotification 5723 INF Monitor 3 event: sending this out as 25 is >= interval of 0 zmeventnotification.pl
2016-02-19 09:54:20.296020 zmeventnotification 5723 INF Checking alarm rules for 0.0.0.0:0 token ending in:...etePKHHtOi zmeventnotification.pl
2016-02-19 09:54:20.295100 zmeventnotification 5723 INF Broadcasting new events to all 1 websocket clients zmeventnotification.pl
2016-02-19 09:54:20.292820 zmeventnotification 5723 INF New event 240 reported for Terrasse zmeventnotification.pl
2016-02-19 09:54:19.981656 zmc_m4 2546 INF Garage: 41000 - Capturing at 22.22 fps zm_monitor.cpp 3131
2016-02-19 09:54:17.770862 zma_m3 2535 INF Terrasse: 10505 - Opening new event 240, alarm start zm_monitor.cpp 1651
2016-02-19 09:54:17.767855 zma_m3 2535 INF Terrasse: 10505 - Gone into alarm state zm_monitor.cpp 1605
2016-02-19 09:54:15.880628 zma_m3 2535 INF Terrasse: 10496 - Gone into prealarm state zm_monitor.cpp 1680
2016-02-19 09:54:13.939668 zma_m3 2535 INF Terrasse: 10485 - Closing event 239, alarm end zm_monitor.cpp 1707
2016-02-19 09:54:13.938607 zma_m3 2535 INF Terrasse: 10485 - Left alarm state (239) - 141(66) images zm_monitor.cpp 1702
2016-02-19 09:54:10.454276 zma_m4 2551 INF Garage: 10000 - Analysing at 5.43 fps zm_monitor.cpp 1287
2016-02-19 09:54:10.185100 zmeventnotification 5723 INF NOT Marking 85.83.32.41 for deletion as token APA91bH7liIqurcA-dvZIVG4iEXVdrepcsjbQ9QbU80jccXnfLuvSeLgojX_6FWx5StndTOo-9Ar2BWzZylXHl2v0XAu7-rPOmJhFEXYYGLxQUIJic0K2sfIGRjd1jDsCJetePKHHtOi active zmeventnotification.pl
2016-02-19 09:54:10.183890 zmeventnotification 5723 INF Websocket remotely disconnected from 85.83.32.41 zmeventnotification.pl
2016-02-19 09:54:06.674314 zmc_m3 5572 INF Terrasse: 6000 - Capturing at 18.52 fps zm_monitor.cpp 3131
2016-02-19 09:54:04.477639 zma_m3 2535 INF Terrasse: 10435 - Gone into alert state zm_monitor.cpp 1695
2016-02-19 09:54:01.463340 zmc_m1 2519 INF Indkorsel: 39000 - Capturing at 21.28 fps zm_monitor.cpp 3131
2016-02-19 09:53:55.296030 zmeventnotification 5723 INF 85.83.32.41-sending over websockets zmeventnotification.pl
2016-02-19 09:53:55.295380 zmeventnotification 5723 INF Monitor 3 event: last time not found, so sending zmeventnotification.pl
2016-02-19 09:53:55.294420 zmeventnotification 5723 INF Checking alarm rules for 85.83.32.41:54809 token ending in:...etePKHHtOi zmeventnotification.pl
2016-02-19 09:53:55.293050 zmeventnotification 5723 INF Broadcasting new events to all 1 websocket clients zmeventnotification.pl
2016-02-19 09:53:55.291570 zmeventnotification 5723 INF New event 239 reported for Terrasse zmeventnotification.pl
2016-02-19 09:53:50.612716 zma_m3 2535 INF Terrasse: 10378 - Opening new event 239, alarm start zm_monitor.cpp 1651
2016-02-19 09:53:50.611010 zma_m3 2535 INF Terrasse: 10378 - Gone into alarm state zm_monitor.cpp 1605
2016-02-19 09:53:48.749175 zma_m3 2535 INF Terrasse: 10369 - Gone into prealarm state zm_monitor.cpp 1680
2016-02-19 09:53:45.380270 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl
2016-02-19 09:53:44.800960 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl
2016-02-19 09:53:44.799090 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl
2016-02-19 09:53:44.214010 zmeventnotification 5723 INF Contrl: Storing token ...etePKHHtOi,monlist:1,3,4,intlist:0,0,0,pushstate:disabled zmeventnotification.pl
2016-02-19 09:53:40.373090 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl
2016-02-19 09:53:39.785070 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl
2016-02-19 09:53:39.783310 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl
2016-02-19 09:53:39.202220 zmeventnotification 5723 INF Contrl: Storing token ...etePKHHtOi,monlist:1,3,4,intlist:0,0,0,pushstate:disabled zmeventnotification.pl
2016-02-19 09:53:37.655280 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl
2016-02-19 09:53:37.074020 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl
2016-02-19 09:53:37.072400 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl
2016-02-19 09:53:36.501696 web_php 2837 INF Login successful for user "admin" /usr/share/zoneminder/includes/functions.php 59
2016-02-19 09:53:36.483200 zmeventnotification 5723 INF Contrl: Storing token ...etePKHHtOi,monlist:1,3,4,intlist:0,0,0,pushstate:disabled zmeventnotification.pl
2016-02-19 09:53:34.961523 zmc_m4 2546 INF Garage: 40000 - Capturing at 22.73 fps zm_monitor.cpp 3131
2016-02-19 09:53:34.911170 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl
2016-02-19 09:53:34.327790 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl
2016-02-19 09:53:34.327150 zmeventnotification 5723 INF Duplicate token found, marking for deletion zmeventnotification.pl
2016-02-19 09:53:34.325960 zmeventnotification 5723 INF Correct authentication provided by 85.83.32.41 zmeventnotification.pl
2016-02-19 09:53:34.314460 zmeventnotification 5723 INF Websockets: New Connection Handshake requested from 85.83.32.41:54809 state=pending auth zmeventnotification.pl
2016-02-19 09:53:34.312020 zmeventnotification 5723 INF got a websocket connection from 85.83.32.41 (1) active connections zmeventnotification.pl
2016-02-19 09:53:23.227340 zmaudit 2584 INF deleting zmaudit.pl
2016-02-19 09:53:23.223190 zmaudit 2584 INF Filesystem event '3/210' does not exist in database zmaudit.pl
2016-02-19 09:53:20.286600 zmeventnotification 5723 INF Web Socket Event Server listening on port 9000 zmeventnotification.pl
2016-02-19 09:53:20.285630 zmeventnotification 5723 INF Secure WS is disabled... zmeventnotification.pl
2016-02-19 09:53:20.284290 zmeventnotification 5723 INF PushProxy https://pliablepixels.ddns.net:8801 is reachable. zmeventnotification.pl
2016-02-19 09:53:18.865110 zmeventnotification 5723 INF Checking https://pliablepixels.ddns.net:8801 reachability... zmeventnotification.pl
2016-02-19 09:53:18.863270 zmeventnotification 5723 INF Loading monitors zmeventnotification.pl
2016-02-19 09:53:18.862720 zmeventnotification 5723 INF Reloading Monitors... zmeventnotification.pl
2016-02-19 09:53:18.861590 zmeventnotification 5723 INF Total event client connections: 1 zmeventnotification.pl
2016-02-19 09:53:18.860470 zmeventnotification 5723 INF Event Notification daemon v 0.7 starting zmeventnotification.pl
2016-02-19 09:53:18.859290 zmeventnotification 5723 INF direct APNS disabled zmeventnotification.pl
2016-02-19 09:53:18.857950 zmeventnotification 5723 INF Push enabled via PushProxy zmeventnotification.pl
2016-02-19 09:53:18.509960 zmdc 5723 INF 'zmeventnotification.pl' started at 16/02/19 09:53:18 zmdc.pl
2016-02-19 09:53:18.509870 zmdc 2477 INF 'zmeventnotification.pl' starting at 16/02/19 09:53:18, pid = 5723 zmdc.pl
Feb 19, 2016 9:55:10 AM DEBUG dis-alarming monitor ID=1
Feb 19, 2016 9:54:50 AM DEBUG App is in foreground, displaying banner
Feb 19, 2016 9:54:50 AM DEBUG Scheduled a 10 sec timer for dis-alarming monitor ID=1
Feb 19, 2016 9:54:50 AM INFO Real-time event: {"status":"Success","type":"","events":[{"EventId":"241","Name":"Terrasse","MonitorId":"3"}],"event":"alarm"}
Feb 19, 2016 9:54:40 AM INFO Stream authentication construction: &auth=d4a07aed227e2b63538e27d50e7226c4
Feb 19, 2016 9:54:40 AM INFO DataModel: Extracted a stream authentication key of: d4a07aed227e2b63538e27d50e7226c4
Feb 19, 2016 9:54:40 AM INFO Real-time event: {"version":"0.7","reason":"","event":"auth","status":"Success","type":""}
Feb 19, 2016 9:54:39 AM DEBUG DataModel: Getting auth from http://
Hey there, I really do need to do a live debug session with you - I am available on IRC #zoneminder on freenode
I actually think your push notification is not reaching my server
On Fri, Feb 19, 2016 at 4:27 AM, Requa3r0 notifications@github.com wrote:
Dear Pliable
Debug logs enabled in zmninja
Running HTC m7 on _ViperOne 9.0.0-fix All_custom rom a android. Its an Android 5.0.2 sense 7.0 enhanced clone http://venomroms.com/htc-one-m7
rooted, x-off and newest firmware...android is all good.
Server
ruining a Debian 8 new install on a powerfull xeon 6 core 12GB ram dell precision t5500 (took the 2 CPU out for power saving reasons..12 cores would have been a bit much ;o)
Its normally running a VPN server and vpn gateway and a complicated firewall + zm..but for the logs i have deniabled all vpn, firwewall and ssl seceriyty..to simplify things, and avoid some strange firewall block etc.
I also copyed the zmeventnotification.pl, changed the few things needed...secrurty and sound = 0 and tokenfile, but no pushover hack.
see logs attached.
I got standard blob when running zmninja..nothing when the app was closed
no icon or notification banner on android..but the bell icon inside zmninja..on blob
on monitor montage..I also get red monitor name.
Hope it will help you in your debugging and development.
Keep up the good work..
let me know if you need more.
One question if you have time
If i wanted to include the monitor name that has an alarm..how would i modify the pushover module...to add this info and add it to the ZM ALARM string
sub pushover{ LWP::UserAgent->new()->post( 'https://api.pushover.net/1/messages.json', [ 'token' => 'yyyyyyy', 'user' => 'xxxxxx',
'sound' => 'sirene',
'priority' => '1', 'message' =>'ZM ALARM on -- monitor name here ----', ]); }
Requaero Those who search shall find
On 14 February 2016 at 00:27, Pliable Pixels notifications@github.com wrote:
zmNinja side: In zmNinja, enable debug logs Exit zmNinja start zmNinja Please send me the Debug logs. Please also mention Android version
ZM side: Please send me logs from zmeventnotification when you generate an event.
— Reply to this email directly or view it on GitHub < https://github.com/pliablepixels/zmNinja/issues/135#issuecomment-183770926
.
!/usr/bin/perl -T
#
#
THIS SCRIPT MUST BE RUN WITH SUDO OR STARTED VIA ZMDC.PL
#
ZoneMinder Realtime Notification System
#
A light weight event notification daemon
Uses shared memory to detect new events (polls SHM)
Also opens a websocket connection at a configurable port
so events can be reported
Any client can connect to this web socket and handle it further
for example, send it out via APNS/GCM or any other mechanism
#
This is a much faster and low overhead method compared to zmfilter
as there is no DB overhead nor SQL searches for event matches
~ PP
#
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
#
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
#
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA. #
sudo perl -MCPAN -e "install Crypt::MySQL"
sudo perl -MCPAN -e "install Net::WebSocket::Server"
For pushProxy
sudo perl -MCPAN -e "install LWP::Protocol::https"
For iOS APNS:
sudo perl -MCPAN -e "install Net::APNS::Persistent"
use File::Basename; use Data::Dumper;
use strict; use bytes;
my $app_version="0.7";
#
These are the elements you can edit to suit your installation
#
use constant EVENT_NOTIFICATION_PORT=>9000; # port for Websockets connection
my $useSecure = 0; # make this 0 if you don't want SSL
ignore if useSecure is 0
use constant SSL_CERT_FILE=>'/etc/apache2/ssl/zoneminder.crt'; # Change these to your certs/keys use constant SSL_KEY_FILE=>'/etc/apache2/ssl/zoneminder.key';
if you only want to enable websockets make both of these 0
my $usePushProxy = 1; # set this to 1 to use a remote push proxy for APNS that I have set up for zmNinja users
my $usePushAPNSDirect = 0; # set this to 1 if you have an APNS SSL certificate/key pair
the only way to have this is if you have an apple developer
account
my $pushProxyURL = 'https://pliablepixels.ddns.net:8801'; # This is my proxy URL. Don't change it unless you are hosting your on APNS AS
my $useCustomNotificationSound = 0; # set to 0 for default sound
PUSH_TOKEN_FILE is needed for pushProxy mode as well as direct APNS mode
change this to a directory and file of your choosing.
This server will create the file if it does not exist
use constant PUSH_TOKEN_FILE=>'/etc/zmninja/tokens.txt'; # MAKE SURE THIS DIRECTORY HAS WWW-DATA PERMISSIONS
-------- There seems to be an LWP perl bug that fails certifying self
signed certs
refer to
https://bugs.launchpad.net/ubuntu/+source/libwww-perl/+bug/1408331
you don't have to make it this drastic, you can also follow other tips
in that thread to point to
a mozilla cert. I haven't tried
my %ssl_push_opts = ( ssl_opts=>{verify_hostname => 0,SSL_verify_mode => 0,SSL_verifycn_scheme => 'none'} );
----------- Start: Change these only if you have usePushAPNSDirect set to
1 ------------------
my $isSandbox = 1; # 1 or 0 depending on your APNS certificate
use constant APNS_CERT_FILE=>'/etc/private/apns-dev-cert.pem'; # only used if usePushAPNSDirect is enabled use constant APNS_KEY_FILE=>'/etc/private/apns-dev-key.pem'; # only used if usePushAPNSDirect is enabled
use constant APNS_FEEDBACK_CHECK_INTERVAL => 3600; # only used if usePushAPNSDirect is enabled
----------- End: only applies to usePushAPNSDirect = 1 --
use constant PUSH_CHECK_REACH_INTERVAL => 3600; # time in seconds to do a reachability test with push proxt use constant SLEEP_DELAY=>5; # duration in seconds after which we will check for new events use constant MONITOR_RELOAD_INTERVAL => 300; use constant WEBSOCKET_AUTH_DELAY => 20; # max seconds by which authentication must be done
These are needed for the remote push to work. Don't change these
use constant PUSHPROXY_APP_NAME => 'zmninjapro'; use constant PUSHPROXY_APP_ID => '3905d86d1f2922ed8f77583058471d70';
use constant PENDING_WEBSOCKET => '1'; use constant INVALID_WEBSOCKET => '-1'; use constant INVALID_APNS => '-2'; use constant VALID_WEBSOCKET => '0';
my $alarmEventId = 1; # tags the event id along with the alarm - useful for correlation
only for geeks though - most people won't give a damn. I do.
if (!try_use ("Net::WebSocket::Server")) {Fatal ("Net::WebSocket::Server missing");exit (-1);} if (!try_use ("IO::Socket::SSL")) {Fatal ("IO::Socket::SSL missing");exit (-1);} if (!try_use ("Crypt::MySQL qw(password password41)")) {Fatal ("Crypt::MySQL missing");exit (-1);}
if (!try_use ("JSON")) { if (!try_use ("JSON::XS")) { Fatal ("JSON or JSON::XS missing");exit (-1);} }
if ($usePushProxy) { if ($usePushAPNSDirect) { $usePushAPNSDirect = 0; Info ("Disabling direct push as push proxy is enabled"); } if (!try_use ("LWP::UserAgent") || !try_use ("URI::URL") || !try_use("LWP::Protocol::https")) { Error ("Disabling PushProxy. PushProxy mode needs LWP::Protocol::https, LWP::UserAgent and URI::URL perl packages installed"); $usePushProxy = 0; } else { Info ("Push enabled via PushProxy"); }
} else { Info ("Push Proxy disabled"); }
These modules are needed only if DirectPush is enabled and PushProxy is
disabled if ($usePushAPNSDirect ) { if (!try_use ("Net::APNS::Persistent") || !try_use ("Net::APNS::Feedback")) { Error ("Net::APNS::Feedback and/or Net::APNS::Persistent not present. Disabling direct APNS support"); $usePushAPNSDirect = 0;
} else { Info ("direct APNS support loaded"); } } else { Info ("direct APNS disabled"); }
#
Don't change anything below here
#
use lib '/usr/local/lib/x86_64-linux-gnu/perl5'; use ZoneMinder; use POSIX; use DBI;
$| = 1;
$ENV{PATH} = '/bin:/usr/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
sub Usage { print( "This daemon is not meant to be invoked from command line\n"); exit( -1 ); }
logInit(); logSetSignal();
my $dbh = zmDbConnect(); my %monitors; my $monitor_reload_time = 0; my $apns_feedback_time = 0; my $proxy_reach_time=0; my $wss; my @events=(); my @active_connections=(); my $alarm_header="";
MAIN
if ($usePushAPNSDirect || $usePushProxy) { my $dir = dirname(PUSH_TOKEN_FILE); if ( ! -d $dir) {
Info ("Creating $dir to store APNS tokens"); mkdir $dir; } }
Info( "Event Notification daemon v $app_version starting\n" ); loadTokens(); initSocketServer(); Info( "Event Notification daemon exiting\n" ); exit();
Try to load a perl module
and if it is not available
generate a log
sub try_use { my $module = shift; eval("use $module"); return($@ ? 0:1); }
This function uses shared memory polling to check if
ZM reported any new events. If it does find events
then the details are packaged into the events array
so they can be JSONified and sent out
sub checkEvents() {
foreach (@active_connections)
{
print "
IP:".$->{conn}->ip().":".$->{conn}->port()."Token:".$_->{token}."\n";
}
my $eventFound = 0; if ( (time() - $monitor_reload_time) > MONITOR_RELOAD_INTERVAL ) { my $len = scalar @active_connections; Info ("Total event client connections: ".$len."\n");
Info ("Reloading Monitors...\n"); foreach my $monitor (values(%monitors)) { zmMemInvalidate( $monitor ); } loadMonitors(); }
@events = (); $alarm_header = ""; foreach my $monitor ( values(%monitors) ) { my ( $state, $last_event ) = zmMemRead( $monitor, [ "shared_data:state", "shared_data:last_event" ] ); if ($state == STATE_ALARM || $state == STATE_ALERT) { if ( !defined($monitor->{LastEvent}) || ($last_event != $monitor->{LastEvent})) { Info( "New event $last_event reported for ".$monitor->{Name}."\n"); $monitor->{LastState} = $state; $monitor->{LastEvent} = $last_event; my $name = $monitor->{Name}; my $mid = $monitor->{Id}; my $eid = $last_event; push @events, {Name => $name, MonitorId => $mid, EventId => $last_event}; $alarm_header = "Alarms: " if (!$alarm_header); $alarm_header = $alarm_header . $name ; $alarm_header = $alarm_header . " (".$last_event.") " if ($alarmEventId); $alarm_header = $alarm_header . "," ; $eventFound = 1; }
} } chop($alarm_header) if ($alarm_header); return ($eventFound); }
Refreshes list of monitors from DB
# sub loadMonitors { Info( "Loading monitors\n" ); $monitor_reload_time = time();
my %new_monitors = ();
my $sql = "SELECT * FROM Monitors WHERE find_in_set( Function, 'Modect,Mocord,Nodect' )" ; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute() or Fatal( "Can't execute: ".$sth->errstr() ); while( my $monitor = $sth->fetchrow_hashref() ) { next if ( !zmMemVerify( $monitor ) ); # Check shared memory ok
if ( defined($monitors{$monitor->{Id}}->{LastState}) ) { $monitor->{LastState} = $monitors{$monitor->{Id}}->{LastState}; } else { $monitor->{LastState} = zmGetMonitorState( $monitor ); } if ( defined($monitors{$monitor->{Id}}->{LastEvent}) ) { $monitor->{LastEvent} = $monitors{$monitor->{Id}}->{LastEvent}; } else { $monitor->{LastEvent} = zmGetLastEvent( $monitor ); } $new_monitors{$monitor->{Id}} = $monitor; } %monitors = %new_monitors; }
Does a health check to make sure push proxy is reachable
sub testProxyURL { if ((time() - $proxy_reach_time) > PUSH_CHECK_REACH_INTERVAL) { Info ("Checking $pushProxyURL reachability..."); my $ua = LWP::UserAgent->new(%ssl_push_opts); $ua->timeout(10); $ua->env_proxy; my $response = $ua->get($pushProxyURL); if ($response->is_success) { Info ("PushProxy $pushProxyURL is reachable.");
} else { Error ($response->status_line); Error ("PushProxy $pushProxyURL is NOT reachable. Notifications will not work. Please reach out to the proxy owner if this error persists"); } $proxy_reach_time = time();
} }
This function compares the password provided over websockets
to the password stored in the ZM MYSQL DB
sub validateZM { my ($u,$p) = @_; return 0 if ( $u eq "" || $p eq ""); my $sql = 'select Password from Users where Username=?'; my $sth = $dbh->prepare_cached($sql) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $u ) or Fatal( "Can't execute: ".$sth->errstr() ); if (my ($state) = $sth->fetchrow_hashref()) { my $encryptedPassword = password41($p); $sth->finish(); return $state->{Password} eq $encryptedPassword ? 1:0; } else { $sth->finish(); return 0; }
}
Passes on device token to the push proxy
sub registerOverPushProxy { my ($token) = shift; my ($platform) = shift; my $uri = $pushProxyURL."/api/v2/tokens"; my $json = '{"device":"'.$platform.'", "token":"'.$token.'", "channel":"default"}'; my $req = HTTP::Request->new ('POST', $uri); $req->header( 'Content-Type' => 'application/json', 'X-AN-APP-NAME'=> PUSHPROXY_APP_NAME, 'X-AN-APP-KEY'=> PUSHPROXY_APP_ID ); $req->content($json); my $lwp = LWP::UserAgent->new(%ssl_push_opts); my $res = $lwp->request( $req ); if ($res->is_success) { Info ("Pushproxy registration success ".$res->content); } else { Warning("Push Proxy Token registration Error:".$res->status_line); }
}
Sends a push notification to the remote proxy
sub sendOverPushProxy {
my ($obj, $header, $str) = @_; $obj->{badge}++; my $uri = $pushProxyURL."/api/v2/push"; my $json;
Not passing full JSON object - so that payload is limited for now
if ($obj->{platform} eq "ios") { if ($useCustomNotificationSound) { $json = '{"device":"'.$obj->{platform}.'", "token":"'.$obj->{token}.'", "alert":"'.$header.'", "sound":"blop.caf", "badge":"'.$obj->{badge}.'"}'; } else { $json = '{"device":"'.$obj->{platform}.'", "token":"'.$obj->{token}.'", "alert":"'.$header.'", "sound":"true", "badge":"'.$obj->{badge}.'"}'; } } else { if ($useCustomNotificationSound) { $json = '{"device":"'.$obj->{platform}.'", "token":"'.$obj->{token}.'", "sound":"blop", "alert":"'.$header.'"}'; } else { $json = '{"device":"'.$obj->{platform}.'", "token":"'.$obj->{token}.'", "alert":"'.$header.'"}'; } }
print "Sending:$json\n";
my $req = HTTP::Request->new ('POST', $uri); $req->header( 'Content-Type' => 'application/json', 'X-AN-APP-NAME'=> PUSHPROXY_APP_NAME, 'X-AN-APP-KEY'=> PUSHPROXY_APP_ID ); $req->content($json); my $lwp = LWP::UserAgent->new(%ssl_push_opts); my $res = $lwp->request( $req ); if ($res->is_success) { Info ("Pushproxy push message success ".$res->content); } else { Info("Push Proxy push message Error:".$res->status_line); } }
This function is called when an alarm
needs to be transmitted over APNS
called only if direct APNS mode is enabled
sub sendOverAPNS { if (!$usePushAPNSDirect) { Info ("Rejecting APNS request as daemon has APNS disabled"); return; }
my ($obj, $header, $str) = @_; my (%hash) = %{$str};
my $apns = Net::APNS::Persistent->new({ sandbox => $isSandbox, cert => APNS_CERT_FILE, key => APNS_KEY_FILE });
$obj->{badge}++; $apns->queue_notification( $obj->{token}, { aps => { alert => $header, sound => 'default', badge => $obj->{badge}, }, alarm_details => \%hash });
$apns->send_queue; $apns->disconnect;
}
This function polls APNS Feedback
to see if any entries need to be removed
only applicable for direct apns mode
sub apnsFeedbackCheck {
if ((time() - $apns_feedback_time) > APNS_FEEDBACK_CHECK_INTERVAL) { if ($usePushProxy) { Info ("Not checking APNS feedback in PushProxy Mode"); return; } if (!$usePushAPNSDirect) { Info ("Rejecting APNS Feedback request as daemon has APNS disabled"); return; }
Info ("Checking APNS Feedback\n"); $apns_feedback_time = time(); my $apnsfb = Net::APNS::Feedback->new({ sandbox => $isSandbox, cert => APNS_CERT_FILE, key => APNS_KEY_FILE }); my @feedback = $apnsfb->retrieve_feedback;
foreach (@feedback[0]->[0]) { my $deletetoken = $->{token}; if ($delete_token != "") { deleteToken($delete_token); foreach(@activeconnections) { if ($->{token} eq $deletetoken) { $->{pending} = INVALID_APNS; Info ("Marking entry as invalid apns token: ". $delete_token."\n"); } } } } } }
This runs at each tick to purge connections
that are inactive or have had an error
This also closes any connection that has not provided
credentials in the time configured after opening a socket
sub checkConnection { foreach (@activeconnections) { my $curtime = time(); if ($->{pending} == PENDING_WEBSOCKET) {
This takes care of purging connections that have not authenticated
if ($curtime - $_->{time} > WEBSOCKET_AUTH_DELAY) {
What happens if auth is not provided but device token is registered?
It may still be a bogus token, so don't risk keeping connection stored
if (exists $->{conn}) { my $conn = $->{conn}; Info ("Rejecting ".$conn->ip()." - authentication timeout"); $_->{pending} = INVALID_WEBSOCKET; my $str = encodejson({event => 'auth', type=>'',status=>'Fail', reason => 'NOAUTH'}); eval {$->{conn}->sendutf8($str);}; $->{conn}->disconnect(); } } }
} @activeconnections = grep { $->{pending} != INVALID_WEBSOCKET } @active_connections; if ($usePushAPNSDirect || $usePushProxy) { @activeconnections = grep { $->{pending} != INVALID_APNS } @active_connections; } }
This function is called whenever we receive a message from a client
sub checkMessage { my ($conn, $msg) = @_;
my $json_string; eval {$json_string = decode_json($msg);}; if ($@) {
my $str = encode_json({event=> 'malformed', type=>'', status=>'Fail', reason=>'BADJSON'}); eval {$conn->send_utf8($str);}; return; }
print "Message:$msg\n";
This event type is when a command related to push notification is
received if (($json_string->{'event'} eq "push") && !$usePushAPNSDirect && !$usePushProxy) { my $str = encode_json({event=>'push', type=>'',status=>'Fail', reason => 'PUSHDISABLED'}); eval {$conn->send_utf8($str);}; return; }
-----------------------------------------------------------------------------------
"push" event processing
-----------------------------------------------------------------------------------
elsif (($json_string->{'event'} eq "push") && ($usePushAPNSDirect || $usePushProxy)) {
sets the unread event count of events for a specific connection
the server keeps a tab of # of events it pushes out per connection
but won't know when the client has read them, so the client call tell
the server
using this message
if ($json_string->{'data'}->{'type'} eq "badge") { foreach (@activeconnections) { if ((exists $->{conn}) && ($->{conn}->ip() eq $conn->ip()) && ($->{conn}->port() eq $conn->port())) {
print "Badge match, setting to 0\n";
$_->{badge} = $json_string->{'data'}->{'badge'}; } } }
This sub type is when a device token is registered
if ($json_string->{'data'}->{'type'} eq "token") {
a token must have a platform otherwise I don't know whether to use APNS
or GCM if (!$json_string->{'data'}->{'platform'}) { my $str = encode_json({event=>'push', type=>'token',status=>'Fail', reason => 'MISSINGPLATFORM'}); eval {$conn->send_utf8($str);}; return; } foreach (@active_connections) {
this token already exists
if ($_->{token} eq $json_string->{'data'}->{'token'}) {
if the token doesn't belong to the same connection
then we have two connections owning the same token
so we need to delete the old one. This can happen when you load
the token from the persistent file and there is no connection
and then the client is loaded
if ( (!exists $->{conn}) || ($->{conn}->ip() ne $conn->ip() && $->{conn}->port() ne $conn->port())) { $->{pending} = INVALID_APNS; Info ("Duplicate token found, marking for deletion");
} else # token matches and connection matches, so it may be an update { $_->{token} = $jsonstring->{'data'}->{'token'}; $->{platform} = $jsonstring->{'data'}->{'platform'}; $->{monlist} = "-1"; $->{intlist} = "-1"; $->{pushstate} = $jsonstring->{'data'}->{'state'}; Info ("Storing token ...".substr($->{token},-10).",monlist:".$->{monlist}.",intlist:".$->{intlist}.",pushstate:".$->{pushstate}."\n"); my ($emonlist,$eintlist) = saveTokens($->{token}, $->{monlist}, $->{intlist}, $->{platform}, $->{pushstate}); $->{monlist} = $emonlist; $->{intlist} = $eintlist; } }
The connection matches but the token does not
this can happen if this is the first token registration after push
notification registration
response is received
elsif ( (exists $->{conn}) && ($->{conn}->ip() eq $conn->ip()) && ($->{conn}->port() eq $conn->port())) { $->{token} = $jsonstring->{'data'}->{'token'}; $->{platform} = $jsonstring->{'data'}->{'platform'}; $->{monlist} = "-1"; $->{intlist} = "-1"; $->{pushstate} = $jsonstring->{'data'}->{'state'}; Info ("Storing token ...".substr($->{token},-10).",monlist:".$->{monlist}.",intlist:".$->{intlist}.",pushstate:".$->{pushstate}."\n"); my ($emonlist,$eintlist) = saveTokens($->{token}, $->{monlist}, $->{intlist}, $->{platform}, $->{pushstate}); $->{monlist} = $emonlist; $->{intlist} = $eintlist;
} }
}
} # event = push
-----------------------------------------------------------------------------------
"control" event processing
-----------------------------------------------------------------------------------
elsif (($json_string->{'event'} eq "control") ) { if ($json_string->{'data'}->{'type'} eq "filter") { if (!$json_string->{'data'}->{'monlist'}) { my $str = encode_json({event=>'control', type=>'filter',status=>'Fail', reason => 'MISSINGMONITORLIST'}); eval {$conn->send_utf8($str);}; return; } if (!$json_string->{'data'}->{'intlist'}) { my $str = encode_json({event=>'control', type=>'filter',status=>'Fail', reason => 'MISSINGINTERVALLIST'}); eval {$conn->send_utf8($str);}; return; } my $monlist = $json_string->{'data'}->{'monlist'}; my $intlist = $json_string->{'data'}->{'intlist'};
print ("CONTROL GOT: $monlist and $intlist\n");
foreach (@activeconnections) { if ((exists $->{conn}) && ($->{conn}->ip() eq $conn->ip()) && ($->{conn}->port() eq $conn->port())) {
$->{monlist} = $monlist; $->{intlist} = $intlist; Info ("Contrl: Storing token ...".substr($->{token},-10).",monlist:".$->{monlist}.",intlist:".$->{intlist}.",pushstate:".$->{pushstate}."\n"); saveTokens($->{token}, $->{monlist}, $->{intlist}, $->{platform}, $_->{pushstate}); } } } if ($json_string->{'data'}->{'type'} eq "version") { foreach (@activeconnections) { if ((exists $->{conn}) && ($->{conn}->ip() eq $conn->ip()) && ($->{conn}->port() eq $conn->port())) { my $str = encode_json({event=>'control',type=>'version', status=>'Success', reason => '', version => $appversion}); eval {$->{conn}->send_utf8($str);};
} } }
} # event = control
-----------------------------------------------------------------------------------
"auth" event processing
-----------------------------------------------------------------------------------
This event type is when a command related to authorization is sent
elsif ($json_string->{'event'} eq "auth") { my $uname = $json_string->{'data'}->{'user'}; my $pwd = $json_string->{'data'}->{'password'};
return if ($uname eq "" || $pwd eq ""); foreach (@activeconnections) { if ( (exists $->{conn}) && ($->{conn}->ip() eq $conn->ip()) && ($->{conn}->port() eq $conn->port()) && ($_->{pending}==PENDING_WEBSOCKET)) { if (!validateZM($uname,$pwd)) {
bad username or password, so reject and mark for deletion
my $str = encodejson({event=>'auth', type=>'', status=>'Fail', reason => 'BADAUTH'}); eval {$->{conn}->sendutf8($str);}; Info("Bad authentication provided by ".$->{conn}->ip()); $_->{pending}=INVALID_WEBSOCKET; } else {
all good, connection auth was valid
$_->{pending}=VALIDWEBSOCKET; $->{token}=''; my $str = encode_json({event=>'auth', type=>'', status=>'Success', reason => '', version => $appversion}); eval {$->{conn}->sendutf8($str);}; Info("Correct authentication provided by ".$->{conn}->ip());
} } } } # event = auth else { my $str = encode_json({event=>$jsonstring->{'event'},type=>'', status=>'Fail', reason => 'NOTSUPPORTED'}); eval {$->{conn}->send_utf8($str);}; } }
This loads APNS tokens stored in a conf file
This ensures even if the daemon dies and
restarts APNS tokens are maintained
I also maintain monitor filter list
so that APNS notifications will only be pushed
for the monitors that are configured against
that token
sub loadTokens { return if (!$usePushAPNSDirect && !$usePushProxy); if ( ! -f PUSH_TOKEN_FILE) { open (my $foh, '>', PUSH_TOKEN_FILE); Info ("Creating ".PUSH_TOKEN_FILE); print $foh ""; close ($foh); }
open (my $fh, '<', PUSH_TOKEN_FILE); chomp( my @lines = <$fh>);
close ($fh); my @uniquetokens = uniq(@lines);
open ($fh, '>', PUSH_TOKEN_FILE);
This makes sure we rewrite the file with
unique tokens
foreach(@uniquetokens) { next if ($ eq ""); print $fh "$\n"; my ($token, $monlist, $intlist, $platform, $pushstate) = split (":",$_);
print "load: PUSHING $row\n";
push @active_connections, { token => $token, pending => VALID_WEBSOCKET, time=>time(), badge => 0, monlist => $monlist, intlist => $intlist, last_sent=>{}, platform => $platform, pushstate => $pushstate };
} close ($fh); }
This is called if the APNS feedback channel
reports an invalid token. We also remove it from
our token file
sub deleteToken { my $dtoken = shift; return if (!$usePushAPNSDirect && !$usePushProxy); return if ( ! -f PUSH_TOKEN_FILE);
open (my $fh, '<', PUSH_TOKEN_FILE); chomp( my @lines = <$fh>); close ($fh); my @uniquetokens = uniq(@lines);
open ($fh, '>', PUSH_TOKEN_FILE);
foreach(@uniquetokens) { my ($token, $monlist, $intlist, $platform, $pushstate) = split (":",$); next if ($ eq "" || $token eq $dtoken); print $fh "$_\n";
print "delete: $row\n";
push @active_connections, { token => $token, pending => VALID_WEBSOCKET, time=>time(), badge => 0, monlist => $monlist, intlist => $intlist, last_sent=>{}, platform => $platform, pushstate => $pushstate };
} close ($fh); }
When a client sends a token id,
I store it in the file
It can be sent multiple times, with or without
monitor list, so I retain the old monitor
list if its not supplied. In the case of zmNinja
tokens are sent without monitor list when the registration
id is received from apple, so we handle that situation
sub saveTokens { return if (!$usePushAPNSDirect && !$usePushProxy); my $stoken = shift; return if ($stoken eq ""); my $smonlist = shift; my $sintlist = shift; my $splatform = shift; my $spushstate = shift; return if ($stoken eq ""); open (my $fh, '<', PUSH_TOKEN_FILE) || Fatal ("Cannot open for read".PUSH_TOKEN_FILE); chomp( my @lines = <$fh>); close ($fh); my @uniquetokens = uniq(@lines); my $found = 0; open (my $fh, '>', PUSH_TOKEN_FILE) || Fatal ("Cannot open for write ".PUSH_TOKENFILE); foreach (@uniquetokens) { next if ($ eq ""); my ($token, $monlist, $intlist, $platform, $pushstate) = split (":",$_); if ($token eq $stoken) { $smonlist = $monlist if ($smonlist eq "-1"); $sintlist = $intlist if ($sintlist eq "-1"); $spushstate = $pushstate if ($spushstate eq ""); print $fh "$stoken:$smonlist:$sintlist:$splatform:$spushstate\n"; $found = 1; } else { $pushstate="enabled" if ($pushstate=""); print $fh "$token:$monlist:$intlist:$platform:$pushstate\n"; }
}
$smonlist = "" if ($smonlist eq "-1"); $sintlist = "" if ($sintlist eq "-1");
print $fh "$stoken:$smonlist:$sintlist:$splatform:$spushstate\n" if (!$found); close ($fh); registerOverPushProxy($stoken,$splatform) if ($usePushProxy);
print "Saved Token $token to file\n";
return ($smonlist, $sintlist);
}
This keeps the latest of any duplicate tokens
we need to ignore monitor list when we do this
sub uniq { my %seen; my @array = reverse @; # we want the latest my @farray=(); foreach (@array) { my ($token,$monlist,$intlist,$platform, $pushstate) = split (":",$);
not interested in monlist & intlist
if (! $seen{$token}++ ) { push @farray, "$token:$monlist:$intlist:$platform:$pushstate"; }
} return @farray;
}
Checks if the monitor for which
an alarm occurred is part of the monitor list
for that connection
sub getInterval { my $intlist = shift; my $monlist = shift; my $mid = shift;
print ("getInterval:MID:$mid INT:$intlist AND MON:$monlist\n");
my @ints = split (',',$intlist); my @mids = split (',',$monlist); my $idx = -1; foreach (@mids) { $idx++;
print ("Comparing $mid with $_\n");
if ($mid eq $_) { last; } }
print ("RETURNING index:$idx with Value:".$ints[$idx]."\n");
return $ints[$idx];
}
Checks if the monitor for which
an alarm occurred is part of the monitor list
for that connection
sub isInList { my $monlist = shift; my $mid = shift;
my @mids = split (',',$monlist); my $found = 0; foreach (@mids) { if ($mid eq $_) { $found = 1; last; } } return $found;
}
sub getIdentity { my $obj=shift; my $identity=""; if (exists $obj->{conn} ) { $identity = $obj->{conn}->ip().":".$obj->{conn}->port(); } if ($obj->{token}) { $identity=$identity." token ending in:...". substr($obj->{token},-10); } $identity="(unknown)" if (!$identity); return $identity; }
This is really the main module
It opens a WSS socket and keeps listening
sub initSocketServer { checkEvents(); testProxyURL() if ($usePushProxy);
my $ssl_server; if ($useSecure) { $ssl_server = IO::Socket::SSL->new( Listen => 10, LocalPort => EVENT_NOTIFICATION_PORT, Proto => 'tcp', Reuse => 1, SSL_cert_file => SSL_CERT_FILE, SSL_key_file => SSL_KEY_FILE ) or die "failed to listen: $!"; Info ("Secure WS(WSS) is enabled..."); } else { Info ("Secure WS is disabled..."); } Info ("Web Socket Event Server listening on port ".EVENT_NOTIFICATION_PORT."\n");
$wss = Net::WebSocket::Server->new( listen => $useSecure ? $ssl_server : EVENT_NOTIFICATION_PORT, tick_period => SLEEP_DELAY, on_tick => sub { checkConnection(); apnsFeedbackCheck() if ($usePushAPNSDirect); testProxyURL() if ($usePushProxy); my $ac = scalar @activeconnections; if (checkEvents()) { Info ("Broadcasting new events to all $ac websocket clients\n"); my ($serv) = @; my $i = 0; foreach (@active_connections) {
Let's see if this connection is interested in this alarm
my $monlist = $->{monlist}; my $intlist = $->{intlist}; my $lastsent = $->{lastsent}; my $obj = $; my $connid = getIdentity($obj); Info ("Checking alarm rules for $connid");
we need to create a per connection array which will be
a subset of main events with the ones that are not in its
monlist left out
my @localevents = (); foreach (@events) { if ($monlist eq "" || isInList($monlist, $->{MonitorId} ) ) { my $mint = getInterval($intlist, $monlist, $->{MonitorId}); my $elapsed; if ($lastsent->{$->{MonitorId}}) { $elapsed = time() - $lastsent->{$->{MonitorId}}; if ($elapsed >= $mint) { Info("Monitor ".$_->{MonitorId}." event: sending this out as $elapsed is
= interval of $mint"); push (@localevents, $_); $lastsent->{$->{MonitorId}} = time(); } else {
Info("Monitor ".$_->{MonitorId}." event: NOT sending this out as $elapsed is less than interval of $mint"); }
} else {
This means we have no record of sending any event to this monitor
$lastsent->{$->{MonitorId}} = time(); Info("Monitor ".$->{MonitorId}." event: last time not found, so sending"); push (@localevents, $); }
}
}
if this array is empty that means none of the alarms
were generated from a monitor it is interested in
next if (scalar @localevents == 0);
my $str = encode_json({event => 'alarm', type=>'', status=>'Success', events => \@localevents}); my $sup_str = encode_json({event => 'alarm', type=>'', status=>'Success', supplementary=>'true', events => \@localevents}); my %hash_str = (event => 'alarm', status=>'Success', events => \@localevents); $i++;
if there is APNS send it over APNS
if not, send it over Websockets
also disabled is a special state which means its registered over push
but it still wants messages over websockets - zmNinja sets this
when websockets override is enabled
if (($->{token} ne "") && ($->{pushstate} ne "disabled" )) { if ($usePushProxy) { Info ("Sending notification over PushProxy"); sendOverPushProxy($_,$alarm_header, $str) ; } elsif ($usePushAPNSDirect) {
Info ("Sending notification directly via APNS"); sendOverAPNS($_,$alarm_header, \%hash_str) ; }
send supplementary event data over websocket
if ($_->{pending} == VALIDWEBSOCKET) { if (exists $->{conn}) { Info ($->{conn}->ip()."-sending supplementary data over websockets\n"); eval {$->{conn}->send_utf8($sup_str);}; if ($@) {
$_->{pending} = INVALID_WEBSOCKET; } } }
}
if there is a websocket send it over websockets
elsif ($_->{pending} == VALIDWEBSOCKET) { if (exists $->{conn}) { Info ($->{conn}->ip()."-sending over websockets\n"); eval {$->{conn}->send_utf8($str);}; if ($@) {
$_->{pending} = INVALID_WEBSOCKET; } } }
}
} },
called when a new connection comes in
onconnect => sub { my ($serv, $conn) = @; my ($len) = scalar @activeconnections; Info ("got a websocket connection from ".$conn->ip()." (". $len.") active connections"); $conn->on( utf8 => sub { my ($conn, $msg) = @; checkMessage($conn, $msg); }, handshake => sub { my ($conn, $handshake) = @_; Info ("Websockets: New Connection Handshake requested from ".$conn->ip().":".$conn->port()." state=pending auth"); my $connect_time = time(); push @active_connections, {conn => $conn, pending => PENDING_WEBSOCKET, time=>$connect_time, monlist => "", intlist => "", lastsent=>{}, platform => "websocket", pushstate => '', badge => 0}; }, disconnect => sub { my ($conn, $code, $reason) = @; Info ("Websocket remotely disconnected from ".$conn->ip()); foreach (@activeconnections) { if ((exists $->{conn}) && ($->{conn}->ip() eq $conn->ip()) && ($->{conn}->port() eq $conn->port())) {
mark this for deletion only if device token
not present
if ( $->{token} eq '') { $->{pending}=INVALID_WEBSOCKET; Info( "Marking ".$conn->ip()." for deletion as websocket closed remotely\n"); } else {
Info( "NOT Marking ".$conn->ip()." for deletion as token ".$_->{token}." active\n"); } }
} }, );
} )->start; } Date/Time Component Server PID Level Message File Line 2016-02-19 09:57:28.939218 zma_m1 2523 INF Indkorsel: 11000 - Analysing at 5.52 fps zm_monitor.cpp 1287 2016-02-19 09:57:20.406754 zmc_m4 2546 INF Garage: 45000 - Capturing at 22.22 fps zm_monitor.cpp 3131 2016-02-19 09:57:14.835598 zma_m4 2551 INF Garage: 11000 - Analysing at 5.43 fps zm_monitor.cpp 1287 2016-02-19 09:57:07.038349 zmc_m1 2519 INF Indkorsel: 43000 - Capturing at 21.28 fps zm_monitor.cpp 3131 2016-02-19 09:56:52.731834 zmc_m3 5572 INF Terrasse: 9000 - Capturing at 18.52 fps zm_monitor.cpp 3131 2016-02-19 09:56:51.672354 web_js 2306 ERR Export request failed: 500 / Internal Server Error - exportFail() ?view=log 2016-02-19 09:56:44.126664 web_js 2746 ERR Export request failed: 500 / Internal Server Error - exportFail() ?view=log 2016-02-19 09:56:35.468823 zmc_m4 2546 INF Garage: 44000 - Capturing at 22.22 fps zm_monitor.cpp 3131 2016-02-19 09:56:20.552037 zmc_m1 2519 INF Indkorsel: 42000 - Capturing at 21.74 fps zm_monitor.cpp 3131 2016-02-19 09:55:58.650479 zmc_m3 5572 INF Terrasse: 8000 - Capturing at 18.18 fps zm_monitor.cpp 3131 2016-02-19 09:55:53.316918 zma_m3 2535 INF Terrasse: 11000 - Analysing at 5.26 fps zm_monitor.cpp 1287 2016-02-19 09:55:50.705142 zmc_m4 2546 INF Garage: 43000 - Capturing at 22.22 fps zm_monitor.cpp 3131 2016-02-19 09:55:34.230602 zmc_m1 2519 INF Indkorsel: 41000 - Capturing at 21.28 fps zm_monitor.cpp 3131 2016-02-19 09:55:08.066938 zma_m3 2535 INF Terrasse: 10742 - Closing event 241, alarm end zm_monitor.cpp 1707 2016-02-19 09:55:08.065944 zma_m3 2535 INF Terrasse: 10742 - Left alarm state (241) - 119(44) images zm_monitor.cpp 1702 2016-02-19 09:55:05.457454 zmc_m4 2546 INF Garage: 42000 - Capturing at 21.74 fps zm_monitor.cpp 3131 2016-02-19 09:55:03.230569 zmc_m3 5572 INF Terrasse: 7000 - Capturing at 17.54 fps zm_monitor.cpp 3131 2016-02-19 09:54:58.530678 zma_m3 2535 INF Terrasse: 10692 - Gone into alert state zm_monitor.cpp 1695 2016-02-19 09:54:50.296660 zmeventnotification 5723 INF 85.83.32.41-sending over websockets zmeventnotification.pl 2016-02-19 09:54:50.295950 zmeventnotification 5723 INF Monitor 3 event: last time not found, so sending zmeventnotification.pl 2016-02-19 09:54:50.295230 zmeventnotification 5723 INF Checking alarm rules for 85.83.32.41:54882 token ending in:...etePKHHtOi zmeventnotification.pl 2016-02-19 09:54:50.293980 zmeventnotification 5723 INF Broadcasting new events to all 1 websocket clients zmeventnotification.pl 2016-02-19 09:54:50.292810 zmeventnotification 5723 INF New event 241 reported for Terrasse zmeventnotification.pl 2016-02-19 09:54:49.792770 zma_m3 2535 INF Terrasse: 10657 - Opening new event 241, alarm start zm_monitor.cpp 1651 2016-02-19 09:54:49.791151 zma_m3 2535 INF Terrasse: 10657 - Gone into alarm state zm_monitor.cpp 1605 2016-02-19 09:54:47.921944 zma_m3 2535 INF Terrasse: 10648 - Gone into prealarm state zm_monitor.cpp 1680 2016-02-19 09:54:47.921293 zmc_m1 2519 INF Indkorsel: 40000 - Capturing at 21.74 fps zm_monitor.cpp 3131 2016-02-19 09:54:43.706839 zma_m3 2535 INF Terrasse: 10624 - Closing event 240, alarm end zm_monitor.cpp 1707 2016-02-19 09:54:43.705903 zma_m3 2535 INF Terrasse: 10624 - Left alarm state (240) - 153(76) images zm_monitor.cpp 1702 2016-02-19 09:54:41.103170 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:54:40.144580 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl 2016-02-19 09:54:40.142400 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:54:39.472330 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl 2016-02-19 09:54:39.470540 zmeventnotification 5723 INF Correct authentication provided by 85.83.32.41 zmeventnotification.pl 2016-02-19 09:54:39.314820 zmeventnotification 5723 INF Websockets: New Connection Handshake requested from 85.83.32.41:54882 state=pending auth zmeventnotification.pl 2016-02-19 09:54:39.312760 zmeventnotification 5723 INF got a websocket connection from 85.83.32.41 (0) active connections zmeventnotification.pl 2016-02-19 09:54:38.751025 web_php 3247 INF Login successful for user "admin" /usr/share/zoneminder/includes/functions.php 59 2016-02-19 09:54:34.232045 zma_m3 2535 INF Terrasse: 10574 - Gone into alert state zm_monitor.cpp 1695 2016-02-19 09:54:27.020207 zma_m1 2523 INF Indkorsel: 10000 - Analysing at 5.46 fps zm_monitor.cpp 1287 2016-02-19 09:54:24.564552 zma_m3 2535 INF Terrasse: 10532 - Gone back into alarm state zm_monitor.cpp 1686 2016-02-19 09:54:24.142570 zma_m3 2535 INF Terrasse: 10530 - Gone into alert state zm_monitor.cpp 1695 2016-02-19 09:54:20.297200 zmeventnotification 5723 INF 0.0.0.0-sending over websockets zmeventnotification.pl 2016-02-19 09:54:20.296610 zmeventnotification 5723 INF Monitor 3 event: sending this out as 25 is >= interval of 0 zmeventnotification.pl 2016-02-19 09:54:20.296020 zmeventnotification 5723 INF Checking alarm rules for 0.0.0.0:0 token ending in:...etePKHHtOi zmeventnotification.pl 2016-02-19 09:54:20.295100 zmeventnotification 5723 INF Broadcasting new events to all 1 websocket clients zmeventnotification.pl 2016-02-19 09:54:20.292820 zmeventnotification 5723 INF New event 240 reported for Terrasse zmeventnotification.pl 2016-02-19 09:54:19.981656 zmc_m4 2546 INF Garage: 41000 - Capturing at 22.22 fps zm_monitor.cpp 3131 2016-02-19 09:54:17.770862 zma_m3 2535 INF Terrasse: 10505 - Opening new event 240, alarm start zm_monitor.cpp 1651 2016-02-19 09:54:17.767855 zma_m3 2535 INF Terrasse: 10505 - Gone into alarm state zm_monitor.cpp 1605 2016-02-19 09:54:15.880628 zma_m3 2535 INF Terrasse: 10496 - Gone into prealarm state zm_monitor.cpp 1680 2016-02-19 09:54:13.939668 zma_m3 2535 INF Terrasse: 10485 - Closing event 239, alarm end zm_monitor.cpp 1707 2016-02-19 09:54:13.938607 zma_m3 2535 INF Terrasse: 10485 - Left alarm state (239) - 141(66) images zm_monitor.cpp 1702 2016-02-19 09:54:10.454276 zma_m4 2551 INF Garage: 10000 - Analysing at 5.43 fps zm_monitor.cpp 1287 2016-02-19 09:54:10.185100 zmeventnotification 5723 INF NOT Marking 85.83.32.41 for deletion as token APA91bH7liIqurcA-dvZIVG4iEXVdrepcsjbQ9QbU80jccXnfLuvSeLgojX_6FWx5StndTOo-9Ar2BWzZylXHl2v0XAu7-rPOmJhFEXYYGLxQUIJic0K2sfIGRjd1jDsCJetePKHHtOi active zmeventnotification.pl 2016-02-19 09:54:10.183890 zmeventnotification 5723 INF Websocket remotely disconnected from 85.83.32.41 zmeventnotification.pl 2016-02-19 09:54:06.674314 zmc_m3 5572 INF Terrasse: 6000 - Capturing at 18.52 fps zm_monitor.cpp 3131 2016-02-19 09:54:04.477639 zma_m3 2535 INF Terrasse: 10435 - Gone into alert state zm_monitor.cpp 1695 2016-02-19 09:54:01.463340 zmc_m1 2519 INF Indkorsel: 39000 - Capturing at 21.28 fps zm_monitor.cpp 3131 2016-02-19 09:53:55.296030 zmeventnotification 5723 INF 85.83.32.41-sending over websockets zmeventnotification.pl 2016-02-19 09:53:55.295380 zmeventnotification 5723 INF Monitor 3 event: last time not found, so sending zmeventnotification.pl 2016-02-19 09:53:55.294420 zmeventnotification 5723 INF Checking alarm rules for 85.83.32.41:54809 token ending in:...etePKHHtOi zmeventnotification.pl 2016-02-19 09:53:55.293050 zmeventnotification 5723 INF Broadcasting new events to all 1 websocket clients zmeventnotification.pl 2016-02-19 09:53:55.291570 zmeventnotification 5723 INF New event 239 reported for Terrasse zmeventnotification.pl 2016-02-19 09:53:50.612716 zma_m3 2535 INF Terrasse: 10378 - Opening new event 239, alarm start zm_monitor.cpp 1651 2016-02-19 09:53:50.611010 zma_m3 2535 INF Terrasse: 10378 - Gone into alarm state zm_monitor.cpp 1605 2016-02-19 09:53:48.749175 zma_m3 2535 INF Terrasse: 10369 - Gone into prealarm state zm_monitor.cpp 1680 2016-02-19 09:53:45.380270 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:53:44.800960 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl 2016-02-19 09:53:44.799090 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:53:44.214010 zmeventnotification 5723 INF Contrl: Storing token ...etePKHHtOi,monlist:1,3,4,intlist:0,0,0,pushstate:disabled zmeventnotification.pl 2016-02-19 09:53:40.373090 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:53:39.785070 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl 2016-02-19 09:53:39.783310 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:53:39.202220 zmeventnotification 5723 INF Contrl: Storing token ...etePKHHtOi,monlist:1,3,4,intlist:0,0,0,pushstate:disabled zmeventnotification.pl 2016-02-19 09:53:37.655280 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:53:37.074020 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl 2016-02-19 09:53:37.072400 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:53:36.501696 web_php 2837 INF Login successful for user "admin" /usr/share/zoneminder/includes/functions.php 59 2016-02-19 09:53:36.483200 zmeventnotification 5723 INF Contrl: Storing token ...etePKHHtOi,monlist:1,3,4,intlist:0,0,0,pushstate:disabled zmeventnotification.pl 2016-02-19 09:53:34.961523 zmc_m4 2546 INF Garage: 40000 - Capturing at 22.73 fps zm_monitor.cpp 3131 2016-02-19 09:53:34.911170 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:53:34.327790 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl 2016-02-19 09:53:34.327150 zmeventnotification 5723 INF Duplicate token found, marking for deletion zmeventnotification.pl 2016-02-19 09:53:34.325960 zmeventnotification 5723 INF Correct authentication provided by 85.83.32.41 zmeventnotification.pl 2016-02-19 09:53:34.314460 zmeventnotification 5723 INF Websockets: New Connection Handshake requested from 85.83.32.41:54809 state=pending auth zmeventnotification.pl 2016-02-19 09:53:34.312020 zmeventnotification 5723 INF got a websocket connection from 85.83.32.41 (1) active connections zmeventnotification.pl 2016-02-19 09:53:23.227340 zmaudit 2584 INF deleting zmaudit.pl 2016-02-19 09:53:23.223190 zmaudit 2584 INF Filesystem event '3/210' does not exist in database zmaudit.pl 2016-02-19 09:53:20.286600 zmeventnotification 5723 INF Web Socket Event Server listening on port 9000 zmeventnotification.pl 2016-02-19 09:53:20.285630 zmeventnotification 5723 INF Secure WS is disabled... zmeventnotification.pl 2016-02-19 09:53:20.284290 zmeventnotification 5723 INF PushProxy https://pliablepixels.ddns.net:8801 is reachable. zmeventnotification.pl 2016-02-19 09:53:18.865110 zmeventnotification 5723 INF Checking https://pliablepixels.ddns.net:8801 reachability... zmeventnotification.pl 2016-02-19 09:53:18.863270 zmeventnotification 5723 INF Loading monitors zmeventnotification.pl 2016-02-19 09:53:18.862720 zmeventnotification 5723 INF Reloading Monitors... zmeventnotification.pl 2016-02-19 09:53:18.861590 zmeventnotification 5723 INF Total event client connections: 1 zmeventnotification.pl 2016-02-19 09:53:18.860470 zmeventnotification 5723 INF Event Notification daemon v 0.7 starting zmeventnotification.pl 2016-02-19 09:53:18.859290 zmeventnotification 5723 INF direct APNS disabled zmeventnotification.pl 2016-02-19 09:53:18.857950 zmeventnotification 5723 INF Push enabled via PushProxy zmeventnotification.pl 2016-02-19 09:53:18.509960 zmdc 5723 INF 'zmeventnotification.pl' started at 16/02/19 09:53:18 zmdc.pl 2016-02-19 09:53:18.509870 zmdc 2477 INF 'zmeventnotification.pl' starting at 16/02/19 09:53:18, pid = 5723 zmdc.pl Feb 19, 2016 9:55:10 AM DEBUG dis-alarming monitor ID=1 Feb 19, 2016 9:54:50 AM DEBUG App is in foreground, displaying banner Feb 19, 2016 9:54:50 AM DEBUG Scheduled a 10 sec timer for dis-alarming monitor ID=1 Feb 19, 2016 9:54:50 AM INFO Real-time event: {"status":"Success","type":"","events":[{"EventId":"241","Name":"Terrasse","MonitorId":"3"}],"event":"alarm"} Feb 19, 2016 9:54:40 AM INFO Stream authentication construction: &auth=d4a07aed227e2b63538e27d50e7226c4 Feb 19, 2016 9:54:40 AM INFO DataModel: Extracted a stream authentication key of: d4a07aed227e2b63538e27d50e7226c4 Feb 19, 2016 9:54:40 AM INFO Real-time event: {"version":"0.7","reason":"","event":"auth","status":"Success","type":""} Feb 19, 2016 9:54:39 AM DEBUG DataModel: Getting auth from http://
/index.php?view=watch&mid=1&connkey=199031 with mid=1 Feb 19, 2016 9:54:39 AM INFO Stored montage order does not exist Feb 19, 2016 9:54:39 AM INFO Inside Montage Ctrl:We found 3 monitors Feb 19, 2016 9:54:39 AM INFO Stored montage order does not exist Feb 19, 2016 9:54:39 AM INFO ConnKey for 4 is :44553 Feb 19, 2016 9:54:39 AM INFO ConnKey for 3 is :38858 Feb 19, 2016 9:54:39 AM INFO ConnKey for 1 is :199031 Feb 19, 2016 9:54:39 AM INFO Monitor load was successful, loaded 3 monitors Feb 19, 2016 9:54:39 AM INFO ZM_EVENT_IMAGE_DIGITS is 5 Feb 19, 2016 9:54:39 AM INFO Got API version: TKK 1.29.0 Feb 19, 2016 9:54:39 AM DEBUG openHandShake: state of push is disabled Feb 19, 2016 9:54:39 AM INFO openHandshake: Websocket open Feb 19, 2016 9:54:39 AM INFO ZM has recaptcha disabled - good Feb 19, 2016 9:54:39 AM INFO Stream authentication construction: Feb 19, 2016 9:54:39 AM INFO getMonitors:Loading all monitors Feb 19, 2016 9:54:39 AM DEBUG getAPIversion called Feb 19, 2016 9:54:39 AM DEBUG Feb 19, 2016 9:54:39 AM DEBUG Config URL for digits is:http:// /api/configs/viewByName/ZM_EVENT_IMAGE_DIGITS.json Feb 19, 2016 9:54:39 AM DEBUG PortalLogin: auth success Feb 19, 2016 9:54:39 AM INFO Deferring auth key, as monitorId unknown Feb 19, 2016 9:54:39 AM DEBUG auth-success emit:Successful Feb 19, 2016 9:54:39 AM INFO zmAutologin successfully logged into Zoneminder Feb 19, 2016 9:54:39 AM INFO Websocket was closed, trying to re-open Feb 19, 2016 9:54:39 AM DEBUG Push Notification registration ID received: {"registrationId":"APA91bH7liIqurcA-dvZIVG4iEXVdrepcsjbQ9QbU80jccXnfLuvSeLgojX_6FWx5StndTOo-9Ar2BWzZylXHl2v0XAu7-rPOmJhFEXYYGLxQUIJic0K2sfIGRjd1jDsCJetePKHHtOi"} Feb 19, 2016 9:54:38 AM INFO Checking if reCaptcha is enabled in ZM... Feb 19, 2016 9:54:38 AM INFO zmAutologin called Feb 19, 2016 9:54:38 AM INFO Cancelling zmAutologin timer Feb 19, 2016 9:54:38 AM DEBUG PIN code not set Feb 19, 2016 9:54:38 AM INFO not checking for touchID Feb 19, 2016 9:54:38 AM INFO User credentials are provided Feb 19, 2016 9:54:38 AM DEBUG Inside Portal login Enter handler Feb 19, 2016 9:54:38 AM INFO zmNinja Version: 1.0.7 Feb 19, 2016 9:54:37 AM INFO Setting up POST LOGIN timer Feb 19, 2016 9:54:37 AM INFO Deleting old log file as it exceeds 20000 bytes 2016-02-19T08:54:37.895Z INFO Checked for update 0 hours ago. Not checking again 2016-02-19T08:54:37.873Z INFO Initializing Websocket with URL ws:// , will connect later... 2016-02-19T08:54:37.866Z INFO Setting up push registration 2016-02-19T08:54:37.864Z DEBUG loginData structure values: {"serverName":"DELLABLO","username":"admin","password":" ","url":"http:// ","apiurl":"http:// /api","eventServer":"ws:// ","maxMontage":"10","streamingurl":"http:// ","maxFPS":"3","montageQuality":"50","singleImageQuality":"50","useSSL":false,"keepAwake":true,"isUseAuth":"1","isUseEventServer":true,"disablePush":true,"eventServerMonitors":"1,3,4","eventServerInterval":"0,0,0","refreshSec":"2","enableDebug":true,"usePin":false,"pinCode":"","canSwipeMonitors":true,"persistMontageOrder":false,"onTapScreen":"events","enableh264":true,"gapless":true,"montageOrder":"","montageHiddenOrder":"","montageArraySize":"1:1:1","graphSize":200,"minAlarmCount":"0","montageSize":"1","useNphZms":true,"packMontage":true,"forceNetworkStop":false,"defaultPushSound":false,"exitOnSleep":false,"enableBlog":true} 2016-02-19T08:54:37.863Z INFO DataModel init recovered this loginData as {"serverName":"DELLABLO","username":"admin","password":" ","url":"http:// ","apiurl":"http:// /api","eventServer":"ws:// ","maxMontage":"10","streamingurl":"http:// ","maxFPS":"3","montageQuality":"50","singleImageQuality":"50","useSSL":false,"keepAwake":true,"isUseAuth":"1","isUseEventServer":true,"disablePush":true,"eventServerMonitors":"1,3,4","eventServerInterval":"0,0,0","refreshSec":"2","enableDebug":true,"usePin":false,"pinCode":"","canSwipeMonitors":true,"persistMontageOrder":false,"onTapScreen":"events","enableh264":true,"gapless":true,"montageOrder":"","montageHiddenOrder":"","montageArraySize":"1:1:1","graphSize":200,"minAlarmCount":"0","montageSize":"1","useNphZms":true,"packMontage":true,"forceNetworkStop":false,"defaultPushSound":false,"exitOnSleep":false,"enableBlog":true} 2016-02-19T08:54:37.860Z INFO ZMData init: checking for stored variables & setting up log file 2016-02-19T08:54:37.859Z INFO You are running on android 2016-02-19T08:54:37.854Z INFO Device is ready — Reply to this email directly or view it on GitHub https://github.com/pliablepixels/zmNinja/issues/135#issuecomment-186131986 .
Hi. I will be away on a skiing holiday for the next week. So I geuss it will have to wait bit.
But just to clear something up...are you using any other ports besides 9000 for the event push to your server?
My linux box is heavily locked down and iptables are configured around a live VPN on tun0, but allowing some stuff...like ssh, zm, zmninja, web interfaces for various demons etc...to pass around that on my global ip, + a lot of local LAN stuff.
Just wondering if I somehow could be blocking the push to your server without knowing..because I am pretty much using a BLOCK ALL...except this and that..kind of setup;O)
The logs from the other day....was with a compleate disabled and reset firewall and closed down VPN..so ..not sure what is going on ;O)
I have logged in to freenode #zoneminder as Requaero just this evening...2230 danish time ...but i did not see you there.
Does this not seems to be the right place.
You have joined the channel Requaero has joined (~requaero@0x55532029.adsl.cybercity.dk) 22:28 Topic: Linux Video Camera and CCTV Security with Motion Detection. | www.zoneminder.com | If you need help, ask and WAIT | #zoneminder-dev for build status | Channel logged at https://botbot.me/irc.freenode.net/zoneminder | 1.29 release on Feb. 3 2016! gnulnx set the topic at: 03 Feb 2016 21:14 Mode: +cnt Created at: 25 Jan 2008 00:19 Requaero Requaero ...just joined..hi all.
On 19 February 2016 at 16:30, Pliable Pixels notifications@github.com wrote:
Hey there, I really do need to do a live debug session with you - I am available on IRC #zoneminder on freenode
I actually think your push notification is not reaching my server
On Fri, Feb 19, 2016 at 4:27 AM, Requa3r0 notifications@github.com wrote:
Dear Pliable
Debug logs enabled in zmninja
Running HTC m7 on _ViperOne 9.0.0-fix All_custom rom a android. Its an
Android 5.0.2 sense 7.0 enhanced clone http://venomroms.com/htc-one-m7
rooted, x-off and newest firmware...android is all good.
Server
ruining a Debian 8 new install on a powerfull xeon 6 core 12GB ram dell precision t5500 (took the 2 CPU out for power saving reasons..12 cores would have been a bit much ;o)
Its normally running a VPN server and vpn gateway and a complicated firewall + zm..but for the logs i have deniabled all vpn, firwewall and ssl seceriyty..to simplify things, and avoid some strange firewall block etc.
I also copyed the zmeventnotification.pl, changed the few things needed...secrurty and sound = 0 and tokenfile, but no pushover hack.
see logs attached.
I got standard blob when running zmninja..nothing when the app was closed
no icon or notification banner on android..but the bell icon inside zmninja..on blob
on monitor montage..I also get red monitor name.
Hope it will help you in your debugging and development.
Keep up the good work..
let me know if you need more.
One question if you have time
If i wanted to include the monitor name that has an alarm..how would i modify the pushover module...to add this info and add it to the ZM ALARM string
sub pushover{ LWP::UserAgent->new()->post( 'https://api.pushover.net/1/messages.json', [ 'token' => 'yyyyyyy', 'user' => 'xxxxxx',
'sound' => 'sirene',
'priority' => '1', 'message' =>'ZM ALARM on -- monitor name here ----', ]); }
Requaero Those who search shall find
On 14 February 2016 at 00:27, Pliable Pixels notifications@github.com wrote:
zmNinja side: In zmNinja, enable debug logs Exit zmNinja start zmNinja Please send me the Debug logs. Please also mention Android version
ZM side: Please send me logs from zmeventnotification when you generate an event.
— Reply to this email directly or view it on GitHub <
https://github.com/pliablepixels/zmNinja/issues/135#issuecomment-183770926
.
!/usr/bin/perl -T
# #
#
THIS SCRIPT MUST BE RUN WITH SUDO OR STARTED VIA ZMDC.PL
#
ZoneMinder Realtime Notification System
#
A light weight event notification daemon
Uses shared memory to detect new events (polls SHM)
Also opens a websocket connection at a configurable port
so events can be reported
Any client can connect to this web socket and handle it further
for example, send it out via APNS/GCM or any other mechanism
#
This is a much faster and low overhead method compared to zmfilter
as there is no DB overhead nor SQL searches for event matches
~ PP
#
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
#
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
#
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA. # #
sudo perl -MCPAN -e "install Crypt::MySQL"
sudo perl -MCPAN -e "install Net::WebSocket::Server"
For pushProxy
sudo perl -MCPAN -e "install LWP::Protocol::https"
For iOS APNS:
sudo perl -MCPAN -e "install Net::APNS::Persistent"
use File::Basename; use Data::Dumper;
use strict; use bytes;
my $app_version="0.7";
#
#
These are the elements you can edit to suit your installation
# #
use constant EVENT_NOTIFICATION_PORT=>9000; # port for Websockets connection
my $useSecure = 0; # make this 0 if you don't want SSL
ignore if useSecure is 0
use constant SSL_CERT_FILE=>'/etc/apache2/ssl/zoneminder.crt'; # Change these to your certs/keys use constant SSL_KEY_FILE=>'/etc/apache2/ssl/zoneminder.key';
if you only want to enable websockets make both of these 0
my $usePushProxy = 1; # set this to 1 to use a remote push proxy for APNS that I have set up for zmNinja users
my $usePushAPNSDirect = 0; # set this to 1 if you have an APNS SSL certificate/key pair
the only way to have this is if you have an apple developer
account
my $pushProxyURL = 'https://pliablepixels.ddns.net:8801'; # This is my proxy URL. Don't change it unless you are hosting your on APNS AS
my $useCustomNotificationSound = 0; # set to 0 for default sound
PUSH_TOKEN_FILE is needed for pushProxy mode as well as direct APNS
mode
change this to a directory and file of your choosing.
This server will create the file if it does not exist
use constant PUSH_TOKEN_FILE=>'/etc/zmninja/tokens.txt'; # MAKE SURE THIS DIRECTORY HAS WWW-DATA PERMISSIONS
-------- There seems to be an LWP perl bug that fails certifying self
signed certs
refer to
https://bugs.launchpad.net/ubuntu/+source/libwww-perl/+bug/1408331
you don't have to make it this drastic, you can also follow other tips
in that thread to point to
a mozilla cert. I haven't tried
my %ssl_push_opts = ( ssl_opts=>{verify_hostname => 0,SSL_verify_mode => 0,SSL_verifycn_scheme => 'none'} );
----------- Start: Change these only if you have usePushAPNSDirect set
to 1 ------------------
my $isSandbox = 1; # 1 or 0 depending on your APNS certificate
use constant APNS_CERT_FILE=>'/etc/private/apns-dev-cert.pem'; # only used if usePushAPNSDirect is enabled use constant APNS_KEY_FILE=>'/etc/private/apns-dev-key.pem'; # only used if usePushAPNSDirect is enabled
use constant APNS_FEEDBACK_CHECK_INTERVAL => 3600; # only used if usePushAPNSDirect is enabled
----------- End: only applies to usePushAPNSDirect = 1 --
use constant PUSH_CHECK_REACH_INTERVAL => 3600; # time in seconds to do a reachability test with push proxt use constant SLEEP_DELAY=>5; # duration in seconds after which we will check for new events use constant MONITOR_RELOAD_INTERVAL => 300; use constant WEBSOCKET_AUTH_DELAY => 20; # max seconds by which authentication must be done
These are needed for the remote push to work. Don't change these
use constant PUSHPROXY_APP_NAME => 'zmninjapro'; use constant PUSHPROXY_APP_ID => '3905d86d1f2922ed8f77583058471d70';
use constant PENDING_WEBSOCKET => '1'; use constant INVALID_WEBSOCKET => '-1'; use constant INVALID_APNS => '-2'; use constant VALID_WEBSOCKET => '0';
my $alarmEventId = 1; # tags the event id along with the alarm - useful for correlation
only for geeks though - most people won't give a damn. I do.
if (!try_use ("Net::WebSocket::Server")) {Fatal ("Net::WebSocket::Server missing");exit (-1);} if (!try_use ("IO::Socket::SSL")) {Fatal ("IO::Socket::SSL missing");exit (-1);} if (!try_use ("Crypt::MySQL qw(password password41)")) {Fatal ("Crypt::MySQL missing");exit (-1);}
if (!try_use ("JSON")) { if (!try_use ("JSON::XS")) { Fatal ("JSON or JSON::XS missing");exit (-1);} }
if ($usePushProxy) { if ($usePushAPNSDirect) { $usePushAPNSDirect = 0; Info ("Disabling direct push as push proxy is enabled"); } if (!try_use ("LWP::UserAgent") || !try_use ("URI::URL") || !try_use("LWP::Protocol::https")) { Error ("Disabling PushProxy. PushProxy mode needs LWP::Protocol::https, LWP::UserAgent and URI::URL perl packages installed"); $usePushProxy = 0; } else { Info ("Push enabled via PushProxy"); }
} else { Info ("Push Proxy disabled"); }
These modules are needed only if DirectPush is enabled and PushProxy is
disabled if ($usePushAPNSDirect ) { if (!try_use ("Net::APNS::Persistent") || !try_use ("Net::APNS::Feedback")) { Error ("Net::APNS::Feedback and/or Net::APNS::Persistent not present. Disabling direct APNS support"); $usePushAPNSDirect = 0;
} else { Info ("direct APNS support loaded"); } } else { Info ("direct APNS disabled"); }
#
#
Don't change anything below here
# #
use lib '/usr/local/lib/x86_64-linux-gnu/perl5'; use ZoneMinder; use POSIX; use DBI;
$| = 1;
$ENV{PATH} = '/bin:/usr/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
sub Usage { print( "This daemon is not meant to be invoked from command line\n"); exit( -1 ); }
logInit(); logSetSignal();
my $dbh = zmDbConnect(); my %monitors; my $monitor_reload_time = 0; my $apns_feedback_time = 0; my $proxy_reach_time=0; my $wss; my @events=(); my @active_connections=(); my $alarm_header="";
MAIN
if ($usePushAPNSDirect || $usePushProxy) { my $dir = dirname(PUSH_TOKEN_FILE); if ( ! -d $dir) {
Info ("Creating $dir to store APNS tokens"); mkdir $dir; } }
Info( "Event Notification daemon v $app_version starting\n" ); loadTokens(); initSocketServer(); Info( "Event Notification daemon exiting\n" ); exit();
Try to load a perl module
and if it is not available
generate a log
sub try_use { my $module = shift; eval("use $module"); return($@ ? 0:1); }
This function uses shared memory polling to check if
ZM reported any new events. If it does find events
then the details are packaged into the events array
so they can be JSONified and sent out
sub checkEvents() {
foreach (@active_connections)
{
print "
IP:".$->{conn}->ip().":".$->{conn}->port()."Token:".$_->{token}."\n";
}
my $eventFound = 0; if ( (time() - $monitor_reload_time) > MONITOR_RELOAD_INTERVAL ) { my $len = scalar @active_connections; Info ("Total event client connections: ".$len."\n");
Info ("Reloading Monitors...\n"); foreach my $monitor (values(%monitors)) { zmMemInvalidate( $monitor ); } loadMonitors(); }
@events = (); $alarm_header = ""; foreach my $monitor ( values(%monitors) ) { my ( $state, $last_event ) = zmMemRead( $monitor, [ "shared_data:state", "shared_data:last_event" ] ); if ($state == STATE_ALARM || $state == STATE_ALERT) { if ( !defined($monitor->{LastEvent}) || ($last_event != $monitor->{LastEvent})) { Info( "New event $last_event reported for ".$monitor->{Name}."\n"); $monitor->{LastState} = $state; $monitor->{LastEvent} = $last_event; my $name = $monitor->{Name}; my $mid = $monitor->{Id}; my $eid = $last_event; push @events, {Name => $name, MonitorId => $mid, EventId => $last_event}; $alarm_header = "Alarms: " if (!$alarm_header); $alarm_header = $alarm_header . $name ; $alarm_header = $alarm_header . " (".$last_event.") " if ($alarmEventId); $alarm_header = $alarm_header . "," ; $eventFound = 1; }
} } chop($alarm_header) if ($alarm_header); return ($eventFound); }
Refreshes list of monitors from DB
# sub loadMonitors { Info( "Loading monitors\n" ); $monitor_reload_time = time();
my %new_monitors = ();
my $sql = "SELECT * FROM Monitors WHERE find_in_set( Function, 'Modect,Mocord,Nodect' )" ; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute() or Fatal( "Can't execute: ".$sth->errstr() ); while( my $monitor = $sth->fetchrow_hashref() ) { next if ( !zmMemVerify( $monitor ) ); # Check shared memory ok
if ( defined($monitors{$monitor->{Id}}->{LastState}) ) { $monitor->{LastState} = $monitors{$monitor->{Id}}->{LastState}; } else { $monitor->{LastState} = zmGetMonitorState( $monitor ); } if ( defined($monitors{$monitor->{Id}}->{LastEvent}) ) { $monitor->{LastEvent} = $monitors{$monitor->{Id}}->{LastEvent}; } else { $monitor->{LastEvent} = zmGetLastEvent( $monitor ); } $new_monitors{$monitor->{Id}} = $monitor; } %monitors = %new_monitors; }
Does a health check to make sure push proxy is reachable
sub testProxyURL { if ((time() - $proxy_reach_time) > PUSH_CHECK_REACH_INTERVAL) { Info ("Checking $pushProxyURL reachability..."); my $ua = LWP::UserAgent->new(%ssl_push_opts); $ua->timeout(10); $ua->env_proxy; my $response = $ua->get($pushProxyURL); if ($response->is_success) { Info ("PushProxy $pushProxyURL is reachable.");
} else { Error ($response->status_line); Error ("PushProxy $pushProxyURL is NOT reachable. Notifications will not work. Please reach out to the proxy owner if this error persists"); } $proxy_reach_time = time();
} }
This function compares the password provided over websockets
to the password stored in the ZM MYSQL DB
sub validateZM { my ($u,$p) = @_; return 0 if ( $u eq "" || $p eq ""); my $sql = 'select Password from Users where Username=?'; my $sth = $dbh->prepare_cached($sql) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $u ) or Fatal( "Can't execute: ".$sth->errstr() ); if (my ($state) = $sth->fetchrow_hashref()) { my $encryptedPassword = password41($p); $sth->finish(); return $state->{Password} eq $encryptedPassword ? 1:0; } else { $sth->finish(); return 0; }
}
Passes on device token to the push proxy
sub registerOverPushProxy { my ($token) = shift; my ($platform) = shift; my $uri = $pushProxyURL."/api/v2/tokens"; my $json = '{"device":"'.$platform.'", "token":"'.$token.'", "channel":"default"}'; my $req = HTTP::Request->new ('POST', $uri); $req->header( 'Content-Type' => 'application/json', 'X-AN-APP-NAME'=> PUSHPROXY_APP_NAME, 'X-AN-APP-KEY'=> PUSHPROXY_APP_ID ); $req->content($json); my $lwp = LWP::UserAgent->new(%ssl_push_opts); my $res = $lwp->request( $req ); if ($res->is_success) { Info ("Pushproxy registration success ".$res->content); } else { Warning("Push Proxy Token registration Error:".$res->status_line); }
}
Sends a push notification to the remote proxy
sub sendOverPushProxy {
my ($obj, $header, $str) = @_; $obj->{badge}++; my $uri = $pushProxyURL."/api/v2/push"; my $json;
Not passing full JSON object - so that payload is limited for now
if ($obj->{platform} eq "ios") { if ($useCustomNotificationSound) { $json = '{"device":"'.$obj->{platform}.'", "token":"'.$obj->{token}.'", "alert":"'.$header.'", "sound":"blop.caf", "badge":"'.$obj->{badge}.'"}'; } else { $json = '{"device":"'.$obj->{platform}.'", "token":"'.$obj->{token}.'", "alert":"'.$header.'", "sound":"true", "badge":"'.$obj->{badge}.'"}'; } } else { if ($useCustomNotificationSound) { $json = '{"device":"'.$obj->{platform}.'", "token":"'.$obj->{token}.'", "sound":"blop", "alert":"'.$header.'"}'; } else { $json = '{"device":"'.$obj->{platform}.'", "token":"'.$obj->{token}.'", "alert":"'.$header.'"}'; } }
print "Sending:$json\n";
my $req = HTTP::Request->new ('POST', $uri); $req->header( 'Content-Type' => 'application/json', 'X-AN-APP-NAME'=> PUSHPROXY_APP_NAME, 'X-AN-APP-KEY'=> PUSHPROXY_APP_ID ); $req->content($json); my $lwp = LWP::UserAgent->new(%ssl_push_opts); my $res = $lwp->request( $req ); if ($res->is_success) { Info ("Pushproxy push message success ".$res->content); } else { Info("Push Proxy push message Error:".$res->status_line); } }
This function is called when an alarm
needs to be transmitted over APNS
called only if direct APNS mode is enabled
sub sendOverAPNS { if (!$usePushAPNSDirect) { Info ("Rejecting APNS request as daemon has APNS disabled"); return; }
my ($obj, $header, $str) = @_; my (%hash) = %{$str};
my $apns = Net::APNS::Persistent->new({ sandbox => $isSandbox, cert => APNS_CERT_FILE, key => APNS_KEY_FILE });
$obj->{badge}++; $apns->queue_notification( $obj->{token}, { aps => { alert => $header, sound => 'default', badge => $obj->{badge}, }, alarm_details => \%hash });
$apns->send_queue; $apns->disconnect;
}
This function polls APNS Feedback
to see if any entries need to be removed
only applicable for direct apns mode
sub apnsFeedbackCheck {
if ((time() - $apns_feedback_time) > APNS_FEEDBACK_CHECK_INTERVAL) { if ($usePushProxy) { Info ("Not checking APNS feedback in PushProxy Mode"); return; } if (!$usePushAPNSDirect) { Info ("Rejecting APNS Feedback request as daemon has APNS disabled"); return; }
Info ("Checking APNS Feedback\n"); $apns_feedback_time = time(); my $apnsfb = Net::APNS::Feedback->new({ sandbox => $isSandbox, cert => APNS_CERT_FILE, key => APNS_KEY_FILE }); my @feedback = $apnsfb->retrieve_feedback;
foreach (@feedback[0]->[0]) { my $deletetoken = $->{token}; if ($delete_token != "") { deleteToken($delete_token); foreach(@activeconnections) { if ($->{token} eq $deletetoken) { $->{pending} = INVALID_APNS; Info ("Marking entry as invalid apns token: ". $delete_token."\n"); } } } } } }
This runs at each tick to purge connections
that are inactive or have had an error
This also closes any connection that has not provided
credentials in the time configured after opening a socket
sub checkConnection { foreach (@activeconnections) { my $curtime = time(); if ($->{pending} == PENDING_WEBSOCKET) {
This takes care of purging connections that have not authenticated
if ($curtime - $_->{time} > WEBSOCKET_AUTH_DELAY) {
What happens if auth is not provided but device token is registered?
It may still be a bogus token, so don't risk keeping connection stored
if (exists $->{conn}) { my $conn = $->{conn}; Info ("Rejecting ".$conn->ip()." - authentication timeout"); $_->{pending} = INVALID_WEBSOCKET; my $str = encodejson({event => 'auth', type=>'',status=>'Fail', reason => 'NOAUTH'}); eval {$->{conn}->sendutf8($str);}; $->{conn}->disconnect(); } } }
} @activeconnections = grep { $->{pending} != INVALID_WEBSOCKET } @active_connections; if ($usePushAPNSDirect || $usePushProxy) { @activeconnections = grep { $->{pending} != INVALID_APNS } @active_connections; } }
This function is called whenever we receive a message from a client
sub checkMessage { my ($conn, $msg) = @_;
my $json_string; eval {$json_string = decode_json($msg);}; if ($@) {
my $str = encode_json({event=> 'malformed', type=>'', status=>'Fail', reason=>'BADJSON'}); eval {$conn->send_utf8($str);}; return; }
print "Message:$msg\n";
This event type is when a command related to push notification is
received if (($json_string->{'event'} eq "push") && !$usePushAPNSDirect && !$usePushProxy) { my $str = encode_json({event=>'push', type=>'',status=>'Fail', reason => 'PUSHDISABLED'}); eval {$conn->send_utf8($str);}; return; }
-----------------------------------------------------------------------------------
"push" event processing
-----------------------------------------------------------------------------------
elsif (($json_string->{'event'} eq "push") && ($usePushAPNSDirect || $usePushProxy)) {
sets the unread event count of events for a specific connection
the server keeps a tab of # of events it pushes out per connection
but won't know when the client has read them, so the client call tell
the server
using this message
if ($json_string->{'data'}->{'type'} eq "badge") { foreach (@activeconnections) { if ((exists $->{conn}) && ($->{conn}->ip() eq $conn->ip()) && ($->{conn}->port() eq $conn->port())) {
print "Badge match, setting to 0\n";
$_->{badge} = $json_string->{'data'}->{'badge'}; } } }
This sub type is when a device token is registered
if ($json_string->{'data'}->{'type'} eq "token") {
a token must have a platform otherwise I don't know whether to use APNS
or GCM if (!$json_string->{'data'}->{'platform'}) { my $str = encode_json({event=>'push', type=>'token',status=>'Fail', reason => 'MISSINGPLATFORM'}); eval {$conn->send_utf8($str);}; return; } foreach (@active_connections) {
this token already exists
if ($_->{token} eq $json_string->{'data'}->{'token'}) {
if the token doesn't belong to the same connection
then we have two connections owning the same token
so we need to delete the old one. This can happen when you load
the token from the persistent file and there is no connection
and then the client is loaded
if ( (!exists $->{conn}) || ($->{conn}->ip() ne $conn->ip() && $->{conn}->port() ne $conn->port())) { $->{pending} = INVALID_APNS; Info ("Duplicate token found, marking for deletion");
} else # token matches and connection matches, so it may be an update { $_->{token} = $jsonstring->{'data'}->{'token'}; $->{platform} = $jsonstring->{'data'}->{'platform'}; $->{monlist} = "-1"; $->{intlist} = "-1"; $->{pushstate} = $json_string->{'data'}->{'state'}; Info ("Storing token
...".substr($->{token},-10).",monlist:".$->{monlist}.",intlist:".$->{intlist}.",pushstate:".$->{pushstate}."\n"); my ($emonlist,$eintlist) = saveTokens($->{token}, $->{monlist}, $->{intlist}, $->{platform}, $->{pushstate}); $->{monlist} = $emonlist; $_->{intlist} = $eintlist; } }
The connection matches but the token does not
this can happen if this is the first token registration after push
notification registration
response is received
elsif ( (exists $->{conn}) && ($->{conn}->ip() eq $conn->ip()) && ($->{conn}->port() eq $conn->port())) { $->{token} = $jsonstring->{'data'}->{'token'}; $->{platform} = $jsonstring->{'data'}->{'platform'}; $->{monlist} = "-1"; $->{intlist} = "-1"; $->{pushstate} = $json_string->{'data'}->{'state'}; Info ("Storing token
...".substr($->{token},-10).",monlist:".$->{monlist}.",intlist:".$->{intlist}.",pushstate:".$->{pushstate}."\n"); my ($emonlist,$eintlist) = saveTokens($->{token}, $->{monlist}, $->{intlist}, $->{platform}, $->{pushstate}); $->{monlist} = $emonlist; $_->{intlist} = $eintlist;
} }
}
} # event = push
-----------------------------------------------------------------------------------
"control" event processing
-----------------------------------------------------------------------------------
elsif (($json_string->{'event'} eq "control") ) { if ($json_string->{'data'}->{'type'} eq "filter") { if (!$json_string->{'data'}->{'monlist'}) { my $str = encode_json({event=>'control', type=>'filter',status=>'Fail', reason => 'MISSINGMONITORLIST'}); eval {$conn->send_utf8($str);}; return; } if (!$json_string->{'data'}->{'intlist'}) { my $str = encode_json({event=>'control', type=>'filter',status=>'Fail', reason => 'MISSINGINTERVALLIST'}); eval {$conn->send_utf8($str);}; return; } my $monlist = $json_string->{'data'}->{'monlist'}; my $intlist = $json_string->{'data'}->{'intlist'};
print ("CONTROL GOT: $monlist and $intlist\n");
foreach (@activeconnections) { if ((exists $->{conn}) && ($->{conn}->ip() eq $conn->ip()) && ($->{conn}->port() eq $conn->port())) {
$->{monlist} = $monlist; $->{intlist} = $intlist; Info ("Contrl: Storing token
...".substr($->{token},-10).",monlist:".$->{monlist}.",intlist:".$->{intlist}.",pushstate:".$->{pushstate}."\n"); saveTokens($->{token}, $->{monlist}, $->{intlist}, $->{platform}, $_->{pushstate}); } } } if ($json_string->{'data'}->{'type'} eq "version") { foreach (@activeconnections) { if ((exists $->{conn}) && ($->{conn}->ip() eq $conn->ip()) && ($->{conn}->port() eq $conn->port())) { my $str = encode_json({event=>'control',type=>'version', status=>'Success', reason => '', version => $appversion}); eval {$->{conn}->send_utf8($str);};
} } }
} # event = control
-----------------------------------------------------------------------------------
"auth" event processing
-----------------------------------------------------------------------------------
This event type is when a command related to authorization is sent
elsif ($json_string->{'event'} eq "auth") { my $uname = $json_string->{'data'}->{'user'}; my $pwd = $json_string->{'data'}->{'password'};
return if ($uname eq "" || $pwd eq ""); foreach (@activeconnections) { if ( (exists $->{conn}) && ($->{conn}->ip() eq $conn->ip()) && ($->{conn}->port() eq $conn->port()) && ($_->{pending}==PENDING_WEBSOCKET)) { if (!validateZM($uname,$pwd)) {
bad username or password, so reject and mark for deletion
my $str = encodejson({event=>'auth', type=>'', status=>'Fail', reason => 'BADAUTH'}); eval {$->{conn}->sendutf8($str);}; Info("Bad authentication provided by ".$->{conn}->ip()); $_->{pending}=INVALID_WEBSOCKET; } else {
all good, connection auth was valid
$_->{pending}=VALIDWEBSOCKET; $->{token}=''; my $str = encode_json({event=>'auth', type=>'', status=>'Success', reason => '', version => $appversion}); eval {$->{conn}->sendutf8($str);}; Info("Correct authentication provided by ".$->{conn}->ip());
} } } } # event = auth else { my $str = encode_json({event=>$jsonstring->{'event'},type=>'', status=>'Fail', reason => 'NOTSUPPORTED'}); eval {$->{conn}->send_utf8($str);}; } }
This loads APNS tokens stored in a conf file
This ensures even if the daemon dies and
restarts APNS tokens are maintained
I also maintain monitor filter list
so that APNS notifications will only be pushed
for the monitors that are configured against
that token
sub loadTokens { return if (!$usePushAPNSDirect && !$usePushProxy); if ( ! -f PUSH_TOKEN_FILE) { open (my $foh, '>', PUSH_TOKEN_FILE); Info ("Creating ".PUSH_TOKEN_FILE); print $foh ""; close ($foh); }
open (my $fh, '<', PUSH_TOKEN_FILE); chomp( my @lines = <$fh>);
close ($fh); my @uniquetokens = uniq(@lines);
open ($fh, '>', PUSH_TOKEN_FILE);
This makes sure we rewrite the file with
unique tokens
foreach(@uniquetokens) { next if ($ eq ""); print $fh "$\n"; my ($token, $monlist, $intlist, $platform, $pushstate) = split (":",$_);
print "load: PUSHING $row\n";
push @active_connections, { token => $token, pending => VALID_WEBSOCKET, time=>time(), badge => 0, monlist => $monlist, intlist => $intlist, last_sent=>{}, platform => $platform, pushstate => $pushstate };
} close ($fh); }
This is called if the APNS feedback channel
reports an invalid token. We also remove it from
our token file
sub deleteToken { my $dtoken = shift; return if (!$usePushAPNSDirect && !$usePushProxy); return if ( ! -f PUSH_TOKEN_FILE);
open (my $fh, '<', PUSH_TOKEN_FILE); chomp( my @lines = <$fh>); close ($fh); my @uniquetokens = uniq(@lines);
open ($fh, '>', PUSH_TOKEN_FILE);
foreach(@uniquetokens) { my ($token, $monlist, $intlist, $platform, $pushstate) = split (":",$); next if ($ eq "" || $token eq $dtoken); print $fh "$_\n";
print "delete: $row\n";
push @active_connections, { token => $token, pending => VALID_WEBSOCKET, time=>time(), badge => 0, monlist => $monlist, intlist => $intlist, last_sent=>{}, platform => $platform, pushstate => $pushstate };
} close ($fh); }
When a client sends a token id,
I store it in the file
It can be sent multiple times, with or without
monitor list, so I retain the old monitor
list if its not supplied. In the case of zmNinja
tokens are sent without monitor list when the registration
id is received from apple, so we handle that situation
sub saveTokens { return if (!$usePushAPNSDirect && !$usePushProxy); my $stoken = shift; return if ($stoken eq ""); my $smonlist = shift; my $sintlist = shift; my $splatform = shift; my $spushstate = shift; return if ($stoken eq ""); open (my $fh, '<', PUSH_TOKEN_FILE) || Fatal ("Cannot open for read".PUSH_TOKEN_FILE); chomp( my @lines = <$fh>); close ($fh); my @uniquetokens = uniq(@lines); my $found = 0; open (my $fh, '>', PUSH_TOKEN_FILE) || Fatal ("Cannot open for write ".PUSH_TOKENFILE); foreach (@uniquetokens) { next if ($ eq ""); my ($token, $monlist, $intlist, $platform, $pushstate) = split (":",$_); if ($token eq $stoken) { $smonlist = $monlist if ($smonlist eq "-1"); $sintlist = $intlist if ($sintlist eq "-1"); $spushstate = $pushstate if ($spushstate eq ""); print $fh "$stoken:$smonlist:$sintlist:$splatform:$spushstate\n"; $found = 1; } else { $pushstate="enabled" if ($pushstate=""); print $fh "$token:$monlist:$intlist:$platform:$pushstate\n"; }
}
$smonlist = "" if ($smonlist eq "-1"); $sintlist = "" if ($sintlist eq "-1");
print $fh "$stoken:$smonlist:$sintlist:$splatform:$spushstate\n" if (!$found); close ($fh); registerOverPushProxy($stoken,$splatform) if ($usePushProxy);
print "Saved Token $token to file\n";
return ($smonlist, $sintlist);
}
This keeps the latest of any duplicate tokens
we need to ignore monitor list when we do this
sub uniq { my %seen; my @array = reverse @; # we want the latest my @farray=(); foreach (@array) { my ($token,$monlist,$intlist,$platform, $pushstate) = split (":",$);
not interested in monlist & intlist
if (! $seen{$token}++ ) { push @farray, "$token:$monlist:$intlist:$platform:$pushstate"; }
} return @farray;
}
Checks if the monitor for which
an alarm occurred is part of the monitor list
for that connection
sub getInterval { my $intlist = shift; my $monlist = shift; my $mid = shift;
print ("getInterval:MID:$mid INT:$intlist AND MON:$monlist\n");
my @ints = split (',',$intlist); my @mids = split (',',$monlist); my $idx = -1; foreach (@mids) { $idx++;
print ("Comparing $mid with $_\n");
if ($mid eq $_) { last; } }
print ("RETURNING index:$idx with Value:".$ints[$idx]."\n");
return $ints[$idx];
}
Checks if the monitor for which
an alarm occurred is part of the monitor list
for that connection
sub isInList { my $monlist = shift; my $mid = shift;
my @mids = split (',',$monlist); my $found = 0; foreach (@mids) { if ($mid eq $_) { $found = 1; last; } } return $found;
}
sub getIdentity { my $obj=shift; my $identity=""; if (exists $obj->{conn} ) { $identity = $obj->{conn}->ip().":".$obj->{conn}->port(); } if ($obj->{token}) { $identity=$identity." token ending in:...". substr($obj->{token},-10); } $identity="(unknown)" if (!$identity); return $identity; }
This is really the main module
It opens a WSS socket and keeps listening
sub initSocketServer { checkEvents(); testProxyURL() if ($usePushProxy);
my $ssl_server; if ($useSecure) { $ssl_server = IO::Socket::SSL->new( Listen => 10, LocalPort => EVENT_NOTIFICATION_PORT, Proto => 'tcp', Reuse => 1, SSL_cert_file => SSL_CERT_FILE, SSL_key_file => SSL_KEY_FILE ) or die "failed to listen: $!"; Info ("Secure WS(WSS) is enabled..."); } else { Info ("Secure WS is disabled..."); } Info ("Web Socket Event Server listening on port ".EVENT_NOTIFICATION_PORT."\n");
$wss = Net::WebSocket::Server->new( listen => $useSecure ? $ssl_server : EVENT_NOTIFICATION_PORT, tick_period => SLEEP_DELAY, on_tick => sub { checkConnection(); apnsFeedbackCheck() if ($usePushAPNSDirect); testProxyURL() if ($usePushProxy); my $ac = scalar @activeconnections; if (checkEvents()) { Info ("Broadcasting new events to all $ac websocket clients\n"); my ($serv) = @; my $i = 0; foreach (@active_connections) {
Let's see if this connection is interested in this alarm
my $monlist = $->{monlist}; my $intlist = $->{intlist}; my $lastsent = $->{lastsent}; my $obj = $; my $connid = getIdentity($obj); Info ("Checking alarm rules for $connid");
we need to create a per connection array which will be
a subset of main events with the ones that are not in its
monlist left out
my @localevents = (); foreach (@events) { if ($monlist eq "" || isInList($monlist, $->{MonitorId} ) ) { my $mint = getInterval($intlist, $monlist, $->{MonitorId}); my $elapsed; if ($lastsent->{$->{MonitorId}}) { $elapsed = time() - $lastsent->{$->{MonitorId}}; if ($elapsed >= $mint) { Info("Monitor ".$_->{MonitorId}." event: sending this out as $elapsed is
= interval of $mint"); push (@localevents, $_); $lastsent->{$->{MonitorId}} = time(); } else {
Info("Monitor ".$_->{MonitorId}." event: NOT sending this out as $elapsed is less than interval of $mint"); }
} else {
This means we have no record of sending any event to this monitor
$lastsent->{$->{MonitorId}} = time(); Info("Monitor ".$->{MonitorId}." event: last time not found, so sending"); push (@localevents, $); }
}
}
if this array is empty that means none of the alarms
were generated from a monitor it is interested in
next if (scalar @localevents == 0);
my $str = encode_json({event => 'alarm', type=>'', status=>'Success', events => \@localevents}); my $sup_str = encode_json({event => 'alarm', type=>'', status=>'Success', supplementary=>'true', events => \@localevents}); my %hash_str = (event => 'alarm', status=>'Success', events => \@localevents); $i++;
if there is APNS send it over APNS
if not, send it over Websockets
also disabled is a special state which means its registered over push
but it still wants messages over websockets - zmNinja sets this
when websockets override is enabled
if (($->{token} ne "") && ($->{pushstate} ne "disabled" )) { if ($usePushProxy) { Info ("Sending notification over PushProxy"); sendOverPushProxy($_,$alarm_header, $str) ; } elsif ($usePushAPNSDirect) {
Info ("Sending notification directly via APNS"); sendOverAPNS($_,$alarm_header, \%hash_str) ; }
send supplementary event data over websocket
if ($_->{pending} == VALIDWEBSOCKET) { if (exists $->{conn}) { Info ($->{conn}->ip()."-sending supplementary data over websockets\n"); eval {$->{conn}->send_utf8($sup_str);}; if ($@) {
$_->{pending} = INVALID_WEBSOCKET; } } }
}
if there is a websocket send it over websockets
elsif ($_->{pending} == VALIDWEBSOCKET) { if (exists $->{conn}) { Info ($->{conn}->ip()."-sending over websockets\n"); eval {$->{conn}->send_utf8($str);}; if ($@) {
$_->{pending} = INVALID_WEBSOCKET; } } }
}
} },
called when a new connection comes in
onconnect => sub { my ($serv, $conn) = @; my ($len) = scalar @activeconnections; Info ("got a websocket connection from ".$conn->ip()." (". $len.") active connections"); $conn->on( utf8 => sub { my ($conn, $msg) = @; checkMessage($conn, $msg); }, handshake => sub { my ($conn, $handshake) = @_; Info ("Websockets: New Connection Handshake requested from ".$conn->ip().":".$conn->port()." state=pending auth"); my $connect_time = time(); push @active_connections, {conn => $conn, pending => PENDING_WEBSOCKET, time=>$connect_time, monlist => "", intlist => "", lastsent=>{}, platform => "websocket", pushstate => '', badge => 0}; }, disconnect => sub { my ($conn, $code, $reason) = @; Info ("Websocket remotely disconnected from ".$conn->ip()); foreach (@activeconnections) { if ((exists $->{conn}) && ($->{conn}->ip() eq $conn->ip()) && ($->{conn}->port() eq $conn->port())) {
mark this for deletion only if device token
not present
if ( $->{token} eq '') { $->{pending}=INVALID_WEBSOCKET; Info( "Marking ".$conn->ip()." for deletion as websocket closed remotely\n"); } else {
Info( "NOT Marking ".$conn->ip()." for deletion as token ".$_->{token}." active\n"); } }
} }, );
} )->start; } Date/Time Component Server PID Level Message File Line 2016-02-19 09:57:28.939218 zma_m1 2523 INF Indkorsel: 11000 - Analysing at 5.52 fps zm_monitor.cpp 1287 2016-02-19 09:57:20.406754 zmc_m4 2546 INF Garage: 45000 - Capturing at 22.22 fps zm_monitor.cpp 3131 2016-02-19 09:57:14.835598 zma_m4 2551 INF Garage: 11000 - Analysing at 5.43 fps zm_monitor.cpp 1287 2016-02-19 09:57:07.038349 zmc_m1 2519 INF Indkorsel: 43000 - Capturing at 21.28 fps zm_monitor.cpp 3131 2016-02-19 09:56:52.731834 zmc_m3 5572 INF Terrasse: 9000 - Capturing at 18.52 fps zm_monitor.cpp 3131 2016-02-19 09:56:51.672354 web_js 2306 ERR Export request failed: 500 / Internal Server Error - exportFail() ?view=log 2016-02-19 09:56:44.126664 web_js 2746 ERR Export request failed: 500 / Internal Server Error - exportFail() ?view=log 2016-02-19 09:56:35.468823 zmc_m4 2546 INF Garage: 44000 - Capturing at 22.22 fps zm_monitor.cpp 3131 2016-02-19 09:56:20.552037 zmc_m1 2519 INF Indkorsel: 42000 - Capturing at 21.74 fps zm_monitor.cpp 3131 2016-02-19 09:55:58.650479 zmc_m3 5572 INF Terrasse: 8000 - Capturing at 18.18 fps zm_monitor.cpp 3131 2016-02-19 09:55:53.316918 zma_m3 2535 INF Terrasse: 11000 - Analysing at 5.26 fps zm_monitor.cpp 1287 2016-02-19 09:55:50.705142 zmc_m4 2546 INF Garage: 43000 - Capturing at 22.22 fps zm_monitor.cpp 3131 2016-02-19 09:55:34.230602 zmc_m1 2519 INF Indkorsel: 41000 - Capturing at 21.28 fps zm_monitor.cpp 3131 2016-02-19 09:55:08.066938 zma_m3 2535 INF Terrasse: 10742 - Closing event 241, alarm end zm_monitor.cpp 1707 2016-02-19 09:55:08.065944 zma_m3 2535 INF Terrasse: 10742 - Left alarm state (241) - 119(44) images zm_monitor.cpp 1702 2016-02-19 09:55:05.457454 zmc_m4 2546 INF Garage: 42000 - Capturing at 21.74 fps zm_monitor.cpp 3131 2016-02-19 09:55:03.230569 zmc_m3 5572 INF Terrasse: 7000 - Capturing at 17.54 fps zm_monitor.cpp 3131 2016-02-19 09:54:58.530678 zma_m3 2535 INF Terrasse: 10692 - Gone into alert state zm_monitor.cpp 1695 2016-02-19 09:54:50.296660 zmeventnotification 5723 INF 85.83.32.41-sending over websockets zmeventnotification.pl 2016-02-19 09:54:50.295950 zmeventnotification 5723 INF Monitor 3 event: last time not found, so sending zmeventnotification.pl 2016-02-19 09:54:50.295230 zmeventnotification 5723 INF Checking alarm rules for 85.83.32.41:54882 token ending in:...etePKHHtOi zmeventnotification.pl 2016-02-19 09:54:50.293980 zmeventnotification 5723 INF Broadcasting new events to all 1 websocket clients zmeventnotification.pl 2016-02-19 09:54:50.292810 zmeventnotification 5723 INF New event 241 reported for Terrasse zmeventnotification.pl 2016-02-19 09:54:49.792770 zma_m3 2535 INF Terrasse: 10657 - Opening new event 241, alarm start zm_monitor.cpp 1651 2016-02-19 09:54:49.791151 zma_m3 2535 INF Terrasse: 10657 - Gone into alarm state zm_monitor.cpp 1605 2016-02-19 09:54:47.921944 zma_m3 2535 INF Terrasse: 10648 - Gone into prealarm state zm_monitor.cpp 1680 2016-02-19 09:54:47.921293 zmc_m1 2519 INF Indkorsel: 40000 - Capturing at 21.74 fps zm_monitor.cpp 3131 2016-02-19 09:54:43.706839 zma_m3 2535 INF Terrasse: 10624 - Closing event 240, alarm end zm_monitor.cpp 1707 2016-02-19 09:54:43.705903 zma_m3 2535 INF Terrasse: 10624 - Left alarm state (240) - 153(76) images zm_monitor.cpp 1702 2016-02-19 09:54:41.103170 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:54:40.144580 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl 2016-02-19 09:54:40.142400 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:54:39.472330 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl 2016-02-19 09:54:39.470540 zmeventnotification 5723 INF Correct authentication provided by 85.83.32.41 zmeventnotification.pl 2016-02-19 09:54:39.314820 zmeventnotification 5723 INF Websockets: New Connection Handshake requested from 85.83.32.41:54882 state=pending auth zmeventnotification.pl 2016-02-19 09:54:39.312760 zmeventnotification 5723 INF got a websocket connection from 85.83.32.41 (0) active connections zmeventnotification.pl 2016-02-19 09:54:38.751025 web_php 3247 INF Login successful for user "admin" /usr/share/zoneminder/includes/functions.php 59 2016-02-19 09:54:34.232045 zma_m3 2535 INF Terrasse: 10574 - Gone into alert state zm_monitor.cpp 1695 2016-02-19 09:54:27.020207 zma_m1 2523 INF Indkorsel: 10000 - Analysing at 5.46 fps zm_monitor.cpp 1287 2016-02-19 09:54:24.564552 zma_m3 2535 INF Terrasse: 10532 - Gone back into alarm state zm_monitor.cpp 1686 2016-02-19 09:54:24.142570 zma_m3 2535 INF Terrasse: 10530 - Gone into alert state zm_monitor.cpp 1695 2016-02-19 09:54:20.297200 zmeventnotification 5723 INF 0.0.0.0-sending over websockets zmeventnotification.pl 2016-02-19 09:54:20.296610 zmeventnotification 5723 INF Monitor 3 event: sending this out as 25 is >= interval of 0 zmeventnotification.pl 2016-02-19 09:54:20.296020 zmeventnotification 5723 INF Checking alarm rules for 0.0.0.0:0 token ending in:...etePKHHtOi zmeventnotification.pl 2016-02-19 09:54:20.295100 zmeventnotification 5723 INF Broadcasting new events to all 1 websocket clients zmeventnotification.pl 2016-02-19 09:54:20.292820 zmeventnotification 5723 INF New event 240 reported for Terrasse zmeventnotification.pl 2016-02-19 09:54:19.981656 zmc_m4 2546 INF Garage: 41000 - Capturing at 22.22 fps zm_monitor.cpp 3131 2016-02-19 09:54:17.770862 zma_m3 2535 INF Terrasse: 10505 - Opening new event 240, alarm start zm_monitor.cpp 1651 2016-02-19 09:54:17.767855 zma_m3 2535 INF Terrasse: 10505 - Gone into alarm state zm_monitor.cpp 1605 2016-02-19 09:54:15.880628 zma_m3 2535 INF Terrasse: 10496 - Gone into prealarm state zm_monitor.cpp 1680 2016-02-19 09:54:13.939668 zma_m3 2535 INF Terrasse: 10485 - Closing event 239, alarm end zm_monitor.cpp 1707 2016-02-19 09:54:13.938607 zma_m3 2535 INF Terrasse: 10485 - Left alarm state (239) - 141(66) images zm_monitor.cpp 1702 2016-02-19 09:54:10.454276 zma_m4 2551 INF Garage: 10000 - Analysing at 5.43 fps zm_monitor.cpp 1287 2016-02-19 09:54:10.185100 zmeventnotification 5723 INF NOT Marking 85.83.32.41 for deletion as token
APA91bH7liIqurcA-dvZIVG4iEXVdrepcsjbQ9QbU80jccXnfLuvSeLgojX_6FWx5StndTOo-9Ar2BWzZylXHl2v0XAu7-rPOmJhFEXYYGLxQUIJic0K2sfIGRjd1jDsCJetePKHHtOi active zmeventnotification.pl 2016-02-19 09:54:10.183890 zmeventnotification 5723 INF Websocket remotely disconnected from 85.83.32.41 zmeventnotification.pl 2016-02-19 09:54:06.674314 zmc_m3 5572 INF Terrasse: 6000 - Capturing at 18.52 fps zm_monitor.cpp 3131 2016-02-19 09:54:04.477639 zma_m3 2535 INF Terrasse: 10435 - Gone into alert state zm_monitor.cpp 1695 2016-02-19 09:54:01.463340 zmc_m1 2519 INF Indkorsel: 39000 - Capturing at 21.28 fps zm_monitor.cpp 3131 2016-02-19 09:53:55.296030 zmeventnotification 5723 INF 85.83.32.41-sending over websockets zmeventnotification.pl 2016-02-19 09:53:55.295380 zmeventnotification 5723 INF Monitor 3 event: last time not found, so sending zmeventnotification.pl 2016-02-19 09:53:55.294420 zmeventnotification 5723 INF Checking alarm rules for 85.83.32.41:54809 token ending in:...etePKHHtOi zmeventnotification.pl 2016-02-19 09:53:55.293050 zmeventnotification 5723 INF Broadcasting new events to all 1 websocket clients zmeventnotification.pl 2016-02-19 09:53:55.291570 zmeventnotification 5723 INF New event 239 reported for Terrasse zmeventnotification.pl 2016-02-19 09:53:50.612716 zma_m3 2535 INF Terrasse: 10378 - Opening new event 239, alarm start zm_monitor.cpp 1651 2016-02-19 09:53:50.611010 zma_m3 2535 INF Terrasse: 10378 - Gone into alarm state zm_monitor.cpp 1605 2016-02-19 09:53:48.749175 zma_m3 2535 INF Terrasse: 10369 - Gone into prealarm state zm_monitor.cpp 1680 2016-02-19 09:53:45.380270 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:53:44.800960 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl 2016-02-19 09:53:44.799090 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:53:44.214010 zmeventnotification 5723 INF Contrl: Storing token ...etePKHHtOi,monlist:1,3,4,intlist:0,0,0,pushstate:disabled zmeventnotification.pl 2016-02-19 09:53:40.373090 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:53:39.785070 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl 2016-02-19 09:53:39.783310 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:53:39.202220 zmeventnotification 5723 INF Contrl: Storing token ...etePKHHtOi,monlist:1,3,4,intlist:0,0,0,pushstate:disabled zmeventnotification.pl 2016-02-19 09:53:37.655280 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:53:37.074020 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl 2016-02-19 09:53:37.072400 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:53:36.501696 web_php 2837 INF Login successful for user "admin" /usr/share/zoneminder/includes/functions.php 59 2016-02-19 09:53:36.483200 zmeventnotification 5723 INF Contrl: Storing token ...etePKHHtOi,monlist:1,3,4,intlist:0,0,0,pushstate:disabled zmeventnotification.pl 2016-02-19 09:53:34.961523 zmc_m4 2546 INF Garage: 40000 - Capturing at 22.73 fps zm_monitor.cpp 3131 2016-02-19 09:53:34.911170 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:53:34.327790 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl 2016-02-19 09:53:34.327150 zmeventnotification 5723 INF Duplicate token found, marking for deletion zmeventnotification.pl 2016-02-19 09:53:34.325960 zmeventnotification 5723 INF Correct authentication provided by 85.83.32.41 zmeventnotification.pl 2016-02-19 09:53:34.314460 zmeventnotification 5723 INF Websockets: New Connection Handshake requested from 85.83.32.41:54809 state=pending auth zmeventnotification.pl 2016-02-19 09:53:34.312020 zmeventnotification 5723 INF got a websocket connection from 85.83.32.41 (1) active connections zmeventnotification.pl 2016-02-19 09:53:23.227340 zmaudit 2584 INF deleting zmaudit.pl 2016-02-19 09:53:23.223190 zmaudit 2584 INF Filesystem event '3/210' does not exist in database zmaudit.pl 2016-02-19 09:53:20.286600 zmeventnotification 5723 INF Web Socket Event Server listening on port 9000 zmeventnotification.pl 2016-02-19 09:53:20.285630 zmeventnotification 5723 INF Secure WS is disabled... zmeventnotification.pl 2016-02-19 09:53:20.284290 zmeventnotification 5723 INF PushProxy https://pliablepixels.ddns.net:8801 is reachable. zmeventnotification.pl 2016-02-19 09:53:18.865110 zmeventnotification 5723 INF Checking https://pliablepixels.ddns.net:8801 reachability... zmeventnotification.pl 2016-02-19 09:53:18.863270 zmeventnotification 5723 INF Loading monitors zmeventnotification.pl 2016-02-19 09:53:18.862720 zmeventnotification 5723 INF Reloading Monitors... zmeventnotification.pl 2016-02-19 09:53:18.861590 zmeventnotification 5723 INF Total event client connections: 1 zmeventnotification.pl 2016-02-19 09:53:18.860470 zmeventnotification 5723 INF Event Notification daemon v 0.7 starting zmeventnotification.pl 2016-02-19 09:53:18.859290 zmeventnotification 5723 INF direct APNS disabled zmeventnotification.pl 2016-02-19 09:53:18.857950 zmeventnotification 5723 INF Push enabled via PushProxy zmeventnotification.pl 2016-02-19 09:53:18.509960 zmdc 5723 INF 'zmeventnotification.pl' started at 16/02/19 09:53:18 zmdc.pl 2016-02-19 09:53:18.509870 zmdc 2477 INF 'zmeventnotification.pl' starting at 16/02/19 09:53:18, pid = 5723 zmdc.pl Feb 19, 2016 9:55:10 AM DEBUG dis-alarming monitor ID=1 Feb 19, 2016 9:54:50 AM DEBUG App is in foreground, displaying banner Feb 19, 2016 9:54:50 AM DEBUG Scheduled a 10 sec timer for dis-alarming monitor ID=1 Feb 19, 2016 9:54:50 AM INFO Real-time event:
{"status":"Success","type":"","events":[{"EventId":"241","Name":"Terrasse","MonitorId":"3"}],"event":"alarm"} Feb 19, 2016 9:54:40 AM INFO Stream authentication construction: &auth=d4a07aed227e2b63538e27d50e7226c4 Feb 19, 2016 9:54:40 AM INFO DataModel: Extracted a stream authentication key of: d4a07aed227e2b63538e27d50e7226c4 Feb 19, 2016 9:54:40 AM INFO Real-time event: {"version":"0.7","reason":"","event":"auth","status":"Success","type":""} Feb 19, 2016 9:54:39 AM DEBUG DataModel: Getting auth from http://
/index.php?view=watch&mid=1&connkey=199031 with mid=1 Feb 19, 2016 9:54:39 AM INFO Stored montage order does not exist Feb 19, 2016 9:54:39 AM INFO Inside Montage Ctrl:We found 3 monitors Feb 19, 2016 9:54:39 AM INFO Stored montage order does not exist Feb 19, 2016 9:54:39 AM INFO ConnKey for 4 is :44553 Feb 19, 2016 9:54:39 AM INFO ConnKey for 3 is :38858 Feb 19, 2016 9:54:39 AM INFO ConnKey for 1 is :199031 Feb 19, 2016 9:54:39 AM INFO Monitor load was successful, loaded 3 monitors Feb 19, 2016 9:54:39 AM INFO ZM_EVENT_IMAGE_DIGITS is 5 Feb 19, 2016 9:54:39 AM INFO Got API version: TKK 1.29.0 Feb 19, 2016 9:54:39 AM DEBUG openHandShake: state of push is disabled Feb 19, 2016 9:54:39 AM INFO openHandshake: Websocket open Feb 19, 2016 9:54:39 AM INFO ZM has recaptcha disabled - good Feb 19, 2016 9:54:39 AM INFO Stream authentication construction: Feb 19, 2016 9:54:39 AM INFO getMonitors:Loading all monitors Feb 19, 2016 9:54:39 AM DEBUG getAPIversion called Feb 19, 2016 9:54:39 AM DEBUG Feb 19, 2016 9:54:39 AM DEBUG Config URL for digits is:http:// /api/configs/viewByName/ZM_EVENT_IMAGE_DIGITS.json Feb 19, 2016 9:54:39 AM DEBUG PortalLogin: auth success Feb 19, 2016 9:54:39 AM INFO Deferring auth key, as monitorId unknown Feb 19, 2016 9:54:39 AM DEBUG auth-success emit:Successful Feb 19, 2016 9:54:39 AM INFO zmAutologin successfully logged into Zoneminder Feb 19, 2016 9:54:39 AM INFO Websocket was closed, trying to re-open Feb 19, 2016 9:54:39 AM DEBUG Push Notification registration ID received: {"registrationId":"APA91bH7liIqurcA-dvZIVG4iEXVdrepcsjbQ9QbU80jccXnfLuvSeLgojX_6FWx5StndTOo-9Ar2BWzZylXHl2v0XAu7-rPOmJhFEXYYGLxQUIJic0K2sfIGRjd1jDsCJetePKHHtOi"} Feb 19, 2016 9:54:38 AM INFO Checking if reCaptcha is enabled in ZM... Feb 19, 2016 9:54:38 AM INFO zmAutologin called Feb 19, 2016 9:54:38 AM INFO Cancelling zmAutologin timer Feb 19, 2016 9:54:38 AM DEBUG PIN code not set Feb 19, 2016 9:54:38 AM INFO not checking for touchID Feb 19, 2016 9:54:38 AM INFO User credentials are provided Feb 19, 2016 9:54:38 AM DEBUG Inside Portal login Enter handler Feb 19, 2016 9:54:38 AM INFO zmNinja Version: 1.0.7 Feb 19, 2016 9:54:37 AM INFO Setting up POST LOGIN timer Feb 19, 2016 9:54:37 AM INFO Deleting old log file as it exceeds 20000 bytes 2016-02-19T08:54:37.895Z INFO Checked for update 0 hours ago. Not checking again 2016-02-19T08:54:37.873Z INFO Initializing Websocket with URL ws:// , will connect later... 2016-02-19T08:54:37.866Z INFO Setting up push registration 2016-02-19T08:54:37.864Z DEBUG loginData structure values: {"serverName":"DELLABLO","username":"admin","password":" ","url":"http:// ","apiurl":"http:// /api","eventServer":"ws:// ","maxMontage":"10","streamingurl":"http:// ","maxFPS":"3","montageQuality":"50","singleImageQuality":"50","useSSL":false,"keepAwake":true,"isUseAuth":"1","isUseEventServer":true,"disablePush":true,"eventServerMonitors":"1,3,4","eventServerInterval":"0,0,0","refreshSec":"2","enableDebug":true,"usePin":false,"pinCode":"","canSwipeMonitors":true,"persistMontageOrder":false,"onTapScreen":"events","enableh264":true,"gapless":true,"montageOrder":"","montageHiddenOrder":"","montageArraySize":"1:1:1","graphSize":200,"minAlarmCount":"0","montageSize":"1","useNphZms":true,"packMontage":true,"forceNetworkStop":false,"defaultPushSound":false,"exitOnSleep":false,"enableBlog":true} 2016-02-19T08:54:37.863Z INFO DataModel init recovered this loginData as {"serverName":"DELLABLO","username":"admin","password":" ","url":"http:// ","apiurl":"http:// /api","eventServer":"ws:// ","maxMontage":"10","streamingurl":"http:// ","maxFPS":"3","montageQuality":"50","singleImageQuality":"50","useSSL":false,"keepAwake":true,"isUseAuth":"1","isUseEventServer":true,"disablePush":true,"eventServerMonitors":"1,3,4","eventServerInterval":"0,0,0","refreshSec":"2","enableDebug":true,"usePin":false,"pinCode":"","canSwipeMonitors":true,"persistMontageOrder":false,"onTapScreen":"events","enableh264":true,"gapless":true,"montageOrder":"","montageHiddenOrder":"","montageArraySize":"1:1:1","graphSize":200,"minAlarmCount":"0","montageSize":"1","useNphZms":true,"packMontage":true,"forceNetworkStop":false,"defaultPushSound":false,"exitOnSleep":false,"enableBlog":true} 2016-02-19T08:54:37.860Z INFO ZMData init: checking for stored variables & setting up log file 2016-02-19T08:54:37.859Z INFO You are running on android 2016-02-19T08:54:37.854Z INFO Device is ready — Reply to this email directly or view it on GitHub < https://github.com/pliablepixels/zmNinja/issues/135#issuecomment-186131986 . — Reply to this email directly or view it on GitHub https://github.com/pliablepixels/zmNinja/issues/135#issuecomment-186260657 .
Hi, I am usually there on weekdays - 9:30AM - 4:30PM US Eastcoast time (I think thats 3:30 PM your time onwards). Hope to catch up with you when you are back on #IRC - thats the right channel
I only use port 9000. If you are not getting push when the app is not running, I'm pretty sure thats because no registration is coming across
thx
On Sat, Feb 20, 2016 at 4:37 PM, Requa3r0 notifications@github.com wrote:
Hi. I will be away on a skiing holiday for the next week. So I geuss it will have to wait bit.
But just to clear something up...are you using any other ports besides 9000 for the event push to your server?
My linux box is heavily locked down and iptables are configured around a live VPN on tun0, but allowing some stuff...like ssh, zm, zmninja, web interfaces for various demons etc...to pass around that on my global ip, + a lot of local LAN stuff.
Just wondering if I somehow could be blocking the push to your server without knowing..because I am pretty much using a BLOCK ALL...except this and that..kind of setup;O)
The logs from the other day....was with a compleate disabled and reset firewall and closed down VPN..so ..not sure what is going on ;O)
I have logged in to freenode #zoneminder as Requaero just this evening...2230 danish time ...but i did not see you there.
Does this not seems to be the right place.
You have joined the channel Requaero has joined (~requaero@0x55532029.adsl.cybercity.dk) 22:28 Topic: Linux Video Camera and CCTV Security with Motion Detection. | www.zoneminder.com | If you need help, ask and WAIT | #zoneminder-dev for build status | Channel logged at https://botbot.me/irc.freenode.net/zoneminder | 1.29 release on Feb. 3 2016! gnulnx set the topic at: 03 Feb 2016 21:14 Mode: +cnt Created at: 25 Jan 2008 00:19 Requaero Requaero ...just joined..hi all.
On 19 February 2016 at 16:30, Pliable Pixels notifications@github.com wrote:
Hey there, I really do need to do a live debug session with you - I am available on IRC #zoneminder on freenode
I actually think your push notification is not reaching my server
On Fri, Feb 19, 2016 at 4:27 AM, Requa3r0 notifications@github.com wrote:
Dear Pliable
Debug logs enabled in zmninja
Running HTC m7 on _ViperOne 9.0.0-fix All_custom rom a android. Its an
Android 5.0.2 sense 7.0 enhanced clone http://venomroms.com/htc-one-m7
rooted, x-off and newest firmware...android is all good.
Server
ruining a Debian 8 new install on a powerfull xeon 6 core 12GB ram dell precision t5500 (took the 2 CPU out for power saving reasons..12 cores would have been a bit much ;o)
Its normally running a VPN server and vpn gateway and a complicated firewall + zm..but for the logs i have deniabled all vpn, firwewall and ssl seceriyty..to simplify things, and avoid some strange firewall block etc.
I also copyed the zmeventnotification.pl, changed the few things needed...secrurty and sound = 0 and tokenfile, but no pushover hack.
see logs attached.
I got standard blob when running zmninja..nothing when the app was closed
no icon or notification banner on android..but the bell icon inside zmninja..on blob
on monitor montage..I also get red monitor name.
Hope it will help you in your debugging and development.
Keep up the good work..
let me know if you need more.
One question if you have time
If i wanted to include the monitor name that has an alarm..how would i modify the pushover module...to add this info and add it to the ZM ALARM string
sub pushover{ LWP::UserAgent->new()->post( 'https://api.pushover.net/1/messages.json', [ 'token' => 'yyyyyyy', 'user' => 'xxxxxx',
'sound' => 'sirene',
'priority' => '1', 'message' =>'ZM ALARM on -- monitor name here ----', ]); }
Requaero Those who search shall find
On 14 February 2016 at 00:27, Pliable Pixels <notifications@github.com
wrote:
zmNinja side: In zmNinja, enable debug logs Exit zmNinja start zmNinja Please send me the Debug logs. Please also mention Android version
ZM side: Please send me logs from zmeventnotification when you generate an event.
— Reply to this email directly or view it on GitHub <
https://github.com/pliablepixels/zmNinja/issues/135#issuecomment-183770926
.
!/usr/bin/perl -T
# #
#
THIS SCRIPT MUST BE RUN WITH SUDO OR STARTED VIA ZMDC.PL
#
ZoneMinder Realtime Notification System
#
A light weight event notification daemon
Uses shared memory to detect new events (polls SHM)
Also opens a websocket connection at a configurable port
so events can be reported
Any client can connect to this web socket and handle it further
for example, send it out via APNS/GCM or any other mechanism
#
This is a much faster and low overhead method compared to zmfilter
as there is no DB overhead nor SQL searches for event matches
~ PP
#
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
#
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
#
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA. # #
sudo perl -MCPAN -e "install Crypt::MySQL"
sudo perl -MCPAN -e "install Net::WebSocket::Server"
For pushProxy
sudo perl -MCPAN -e "install LWP::Protocol::https"
For iOS APNS:
sudo perl -MCPAN -e "install Net::APNS::Persistent"
use File::Basename; use Data::Dumper;
use strict; use bytes;
my $app_version="0.7";
#
#
These are the elements you can edit to suit your installation
# #
use constant EVENT_NOTIFICATION_PORT=>9000; # port for Websockets connection
my $useSecure = 0; # make this 0 if you don't want SSL
ignore if useSecure is 0
use constant SSL_CERT_FILE=>'/etc/apache2/ssl/zoneminder.crt'; # Change these to your certs/keys use constant SSL_KEY_FILE=>'/etc/apache2/ssl/zoneminder.key';
if you only want to enable websockets make both of these 0
my $usePushProxy = 1; # set this to 1 to use a remote push proxy for APNS that I have set up for zmNinja users
my $usePushAPNSDirect = 0; # set this to 1 if you have an APNS SSL certificate/key pair
the only way to have this is if you have an apple developer
account
my $pushProxyURL = 'https://pliablepixels.ddns.net:8801'; # This is my proxy URL. Don't change it unless you are hosting your on APNS AS
my $useCustomNotificationSound = 0; # set to 0 for default sound
PUSH_TOKEN_FILE is needed for pushProxy mode as well as direct APNS
mode
change this to a directory and file of your choosing.
This server will create the file if it does not exist
use constant PUSH_TOKEN_FILE=>'/etc/zmninja/tokens.txt'; # MAKE SURE THIS DIRECTORY HAS WWW-DATA PERMISSIONS
-------- There seems to be an LWP perl bug that fails certifying self
signed certs
refer to
https://bugs.launchpad.net/ubuntu/+source/libwww-perl/+bug/1408331
you don't have to make it this drastic, you can also follow other
tips in that thread to point to
a mozilla cert. I haven't tried
my %ssl_push_opts = ( ssl_opts=>{verify_hostname => 0,SSL_verify_mode => 0,SSL_verifycn_scheme => 'none'} );
----------- Start: Change these only if you have usePushAPNSDirect set
to 1 ------------------
my $isSandbox = 1; # 1 or 0 depending on your APNS certificate
use constant APNS_CERT_FILE=>'/etc/private/apns-dev-cert.pem'; # only used if usePushAPNSDirect is enabled use constant APNS_KEY_FILE=>'/etc/private/apns-dev-key.pem'; # only used if usePushAPNSDirect is enabled
use constant APNS_FEEDBACK_CHECK_INTERVAL => 3600; # only used if usePushAPNSDirect is enabled
----------- End: only applies to usePushAPNSDirect = 1 --
use constant PUSH_CHECK_REACH_INTERVAL => 3600; # time in seconds to do a reachability test with push proxt use constant SLEEP_DELAY=>5; # duration in seconds after which we will check for new events use constant MONITOR_RELOAD_INTERVAL => 300; use constant WEBSOCKET_AUTH_DELAY => 20; # max seconds by which authentication must be done
These are needed for the remote push to work. Don't change these
use constant PUSHPROXY_APP_NAME => 'zmninjapro'; use constant PUSHPROXY_APP_ID => '3905d86d1f2922ed8f77583058471d70';
use constant PENDING_WEBSOCKET => '1'; use constant INVALID_WEBSOCKET => '-1'; use constant INVALID_APNS => '-2'; use constant VALID_WEBSOCKET => '0';
my $alarmEventId = 1; # tags the event id along with the alarm - useful for correlation
only for geeks though - most people won't give a damn. I do.
if (!try_use ("Net::WebSocket::Server")) {Fatal ("Net::WebSocket::Server missing");exit (-1);} if (!try_use ("IO::Socket::SSL")) {Fatal ("IO::Socket::SSL missing");exit (-1);} if (!try_use ("Crypt::MySQL qw(password password41)")) {Fatal ("Crypt::MySQL missing");exit (-1);}
if (!try_use ("JSON")) { if (!try_use ("JSON::XS")) { Fatal ("JSON or JSON::XS missing");exit (-1);} }
if ($usePushProxy) { if ($usePushAPNSDirect) { $usePushAPNSDirect = 0; Info ("Disabling direct push as push proxy is enabled"); } if (!try_use ("LWP::UserAgent") || !try_use ("URI::URL") || !try_use("LWP::Protocol::https")) { Error ("Disabling PushProxy. PushProxy mode needs LWP::Protocol::https, LWP::UserAgent and URI::URL perl packages installed"); $usePushProxy = 0; } else { Info ("Push enabled via PushProxy"); }
} else { Info ("Push Proxy disabled"); }
These modules are needed only if DirectPush is enabled and PushProxy
is disabled if ($usePushAPNSDirect ) { if (!try_use ("Net::APNS::Persistent") || !try_use ("Net::APNS::Feedback")) { Error ("Net::APNS::Feedback and/or Net::APNS::Persistent not present. Disabling direct APNS support"); $usePushAPNSDirect = 0;
} else { Info ("direct APNS support loaded"); } } else { Info ("direct APNS disabled"); }
#
#
Don't change anything below here
# #
use lib '/usr/local/lib/x86_64-linux-gnu/perl5'; use ZoneMinder; use POSIX; use DBI;
$| = 1;
$ENV{PATH} = '/bin:/usr/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
sub Usage { print( "This daemon is not meant to be invoked from command line\n"); exit( -1 ); }
logInit(); logSetSignal();
my $dbh = zmDbConnect(); my %monitors; my $monitor_reload_time = 0; my $apns_feedback_time = 0; my $proxy_reach_time=0; my $wss; my @events=(); my @active_connections=(); my $alarm_header="";
MAIN
if ($usePushAPNSDirect || $usePushProxy) { my $dir = dirname(PUSH_TOKEN_FILE); if ( ! -d $dir) {
Info ("Creating $dir to store APNS tokens"); mkdir $dir; } }
Info( "Event Notification daemon v $app_version starting\n" ); loadTokens(); initSocketServer(); Info( "Event Notification daemon exiting\n" ); exit();
Try to load a perl module
and if it is not available
generate a log
sub try_use { my $module = shift; eval("use $module"); return($@ ? 0:1); }
This function uses shared memory polling to check if
ZM reported any new events. If it does find events
then the details are packaged into the events array
so they can be JSONified and sent out
sub checkEvents() {
foreach (@active_connections)
{
print "
IP:".$->{conn}->ip().":".$->{conn}->port()."Token:".$_->{token}."\n";
}
my $eventFound = 0; if ( (time() - $monitor_reload_time) > MONITOR_RELOAD_INTERVAL ) { my $len = scalar @active_connections; Info ("Total event client connections: ".$len."\n");
Info ("Reloading Monitors...\n"); foreach my $monitor (values(%monitors)) { zmMemInvalidate( $monitor ); } loadMonitors(); }
@events = (); $alarm_header = ""; foreach my $monitor ( values(%monitors) ) { my ( $state, $last_event ) = zmMemRead( $monitor, [ "shared_data:state", "shared_data:last_event" ] ); if ($state == STATE_ALARM || $state == STATE_ALERT) { if ( !defined($monitor->{LastEvent}) || ($last_event != $monitor->{LastEvent})) { Info( "New event $last_event reported for ".$monitor->{Name}."\n"); $monitor->{LastState} = $state; $monitor->{LastEvent} = $last_event; my $name = $monitor->{Name}; my $mid = $monitor->{Id}; my $eid = $last_event; push @events, {Name => $name, MonitorId => $mid, EventId => $last_event}; $alarm_header = "Alarms: " if (!$alarm_header); $alarm_header = $alarm_header . $name ; $alarm_header = $alarm_header . " (".$last_event.") " if ($alarmEventId); $alarm_header = $alarm_header . "," ; $eventFound = 1; }
} } chop($alarm_header) if ($alarm_header); return ($eventFound); }
Refreshes list of monitors from DB
# sub loadMonitors { Info( "Loading monitors\n" ); $monitor_reload_time = time();
my %new_monitors = ();
my $sql = "SELECT * FROM Monitors WHERE find_in_set( Function, 'Modect,Mocord,Nodect' )" ; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute() or Fatal( "Can't execute: ".$sth->errstr() ); while( my $monitor = $sth->fetchrow_hashref() ) { next if ( !zmMemVerify( $monitor ) ); # Check shared memory ok
if ( defined($monitors{$monitor->{Id}}->{LastState}) ) { $monitor->{LastState} = $monitors{$monitor->{Id}}->{LastState}; } else { $monitor->{LastState} = zmGetMonitorState( $monitor ); } if ( defined($monitors{$monitor->{Id}}->{LastEvent}) ) { $monitor->{LastEvent} = $monitors{$monitor->{Id}}->{LastEvent}; } else { $monitor->{LastEvent} = zmGetLastEvent( $monitor ); } $new_monitors{$monitor->{Id}} = $monitor; } %monitors = %new_monitors; }
Does a health check to make sure push proxy is reachable
sub testProxyURL { if ((time() - $proxy_reach_time) > PUSH_CHECK_REACH_INTERVAL) { Info ("Checking $pushProxyURL reachability..."); my $ua = LWP::UserAgent->new(%ssl_push_opts); $ua->timeout(10); $ua->env_proxy; my $response = $ua->get($pushProxyURL); if ($response->is_success) { Info ("PushProxy $pushProxyURL is reachable.");
} else { Error ($response->status_line); Error ("PushProxy $pushProxyURL is NOT reachable. Notifications will not work. Please reach out to the proxy owner if this error persists"); } $proxy_reach_time = time();
} }
This function compares the password provided over websockets
to the password stored in the ZM MYSQL DB
sub validateZM { my ($u,$p) = @_; return 0 if ( $u eq "" || $p eq ""); my $sql = 'select Password from Users where Username=?'; my $sth = $dbh->prepare_cached($sql) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $u ) or Fatal( "Can't execute: ".$sth->errstr() ); if (my ($state) = $sth->fetchrow_hashref()) { my $encryptedPassword = password41($p); $sth->finish(); return $state->{Password} eq $encryptedPassword ? 1:0; } else { $sth->finish(); return 0; }
}
Passes on device token to the push proxy
sub registerOverPushProxy { my ($token) = shift; my ($platform) = shift; my $uri = $pushProxyURL."/api/v2/tokens"; my $json = '{"device":"'.$platform.'", "token":"'.$token.'", "channel":"default"}'; my $req = HTTP::Request->new ('POST', $uri); $req->header( 'Content-Type' => 'application/json', 'X-AN-APP-NAME'=> PUSHPROXY_APP_NAME, 'X-AN-APP-KEY'=> PUSHPROXY_APP_ID ); $req->content($json); my $lwp = LWP::UserAgent->new(%ssl_push_opts); my $res = $lwp->request( $req ); if ($res->is_success) { Info ("Pushproxy registration success ".$res->content); } else { Warning("Push Proxy Token registration Error:".$res->status_line); }
}
Sends a push notification to the remote proxy
sub sendOverPushProxy {
my ($obj, $header, $str) = @_; $obj->{badge}++; my $uri = $pushProxyURL."/api/v2/push"; my $json;
Not passing full JSON object - so that payload is limited for now
if ($obj->{platform} eq "ios") { if ($useCustomNotificationSound) { $json = '{"device":"'.$obj->{platform}.'", "token":"'.$obj->{token}.'", "alert":"'.$header.'", "sound":"blop.caf", "badge":"'.$obj->{badge}.'"}'; } else { $json = '{"device":"'.$obj->{platform}.'", "token":"'.$obj->{token}.'", "alert":"'.$header.'", "sound":"true", "badge":"'.$obj->{badge}.'"}'; } } else { if ($useCustomNotificationSound) { $json = '{"device":"'.$obj->{platform}.'", "token":"'.$obj->{token}.'", "sound":"blop", "alert":"'.$header.'"}'; } else { $json = '{"device":"'.$obj->{platform}.'", "token":"'.$obj->{token}.'", "alert":"'.$header.'"}'; } }
print "Sending:$json\n";
my $req = HTTP::Request->new ('POST', $uri); $req->header( 'Content-Type' => 'application/json', 'X-AN-APP-NAME'=> PUSHPROXY_APP_NAME, 'X-AN-APP-KEY'=> PUSHPROXY_APP_ID ); $req->content($json); my $lwp = LWP::UserAgent->new(%ssl_push_opts); my $res = $lwp->request( $req ); if ($res->is_success) { Info ("Pushproxy push message success ".$res->content); } else { Info("Push Proxy push message Error:".$res->status_line); } }
This function is called when an alarm
needs to be transmitted over APNS
called only if direct APNS mode is enabled
sub sendOverAPNS { if (!$usePushAPNSDirect) { Info ("Rejecting APNS request as daemon has APNS disabled"); return; }
my ($obj, $header, $str) = @_; my (%hash) = %{$str};
my $apns = Net::APNS::Persistent->new({ sandbox => $isSandbox, cert => APNS_CERT_FILE, key => APNS_KEY_FILE });
$obj->{badge}++; $apns->queue_notification( $obj->{token}, { aps => { alert => $header, sound => 'default', badge => $obj->{badge}, }, alarm_details => \%hash });
$apns->send_queue; $apns->disconnect;
}
This function polls APNS Feedback
to see if any entries need to be removed
only applicable for direct apns mode
sub apnsFeedbackCheck {
if ((time() - $apns_feedback_time) > APNS_FEEDBACK_CHECK_INTERVAL) { if ($usePushProxy) { Info ("Not checking APNS feedback in PushProxy Mode"); return; } if (!$usePushAPNSDirect) { Info ("Rejecting APNS Feedback request as daemon has APNS disabled"); return; }
Info ("Checking APNS Feedback\n"); $apns_feedback_time = time(); my $apnsfb = Net::APNS::Feedback->new({ sandbox => $isSandbox, cert => APNS_CERT_FILE, key => APNS_KEY_FILE }); my @feedback = $apnsfb->retrieve_feedback;
foreach (@feedback[0]->[0]) { my $deletetoken = $->{token}; if ($delete_token != "") { deleteToken($delete_token); foreach(@activeconnections) { if ($->{token} eq $deletetoken) { $->{pending} = INVALID_APNS; Info ("Marking entry as invalid apns token: ". $delete_token."\n"); } } } } } }
This runs at each tick to purge connections
that are inactive or have had an error
This also closes any connection that has not provided
credentials in the time configured after opening a socket
sub checkConnection { foreach (@activeconnections) { my $curtime = time(); if ($->{pending} == PENDING_WEBSOCKET) {
This takes care of purging connections that have not authenticated
if ($curtime - $_->{time} > WEBSOCKET_AUTH_DELAY) {
What happens if auth is not provided but device token is registered?
It may still be a bogus token, so don't risk keeping connection
stored if (exists $->{conn}) { my $conn = $->{conn}; Info ("Rejecting ".$conn->ip()." - authentication timeout"); $_->{pending} = INVALID_WEBSOCKET; my $str = encodejson({event => 'auth', type=>'',status=>'Fail', reason => 'NOAUTH'}); eval {$->{conn}->sendutf8($str);}; $->{conn}->disconnect(); } } }
} @activeconnections = grep { $->{pending} != INVALID_WEBSOCKET } @active_connections; if ($usePushAPNSDirect || $usePushProxy) { @activeconnections = grep { $->{pending} != INVALID_APNS } @active_connections; } }
This function is called whenever we receive a message from a client
sub checkMessage { my ($conn, $msg) = @_;
my $json_string; eval {$json_string = decode_json($msg);}; if ($@) {
my $str = encode_json({event=> 'malformed', type=>'', status=>'Fail', reason=>'BADJSON'}); eval {$conn->send_utf8($str);}; return; }
print "Message:$msg\n";
This event type is when a command related to push notification is
received if (($json_string->{'event'} eq "push") && !$usePushAPNSDirect && !$usePushProxy) { my $str = encode_json({event=>'push', type=>'',status=>'Fail', reason => 'PUSHDISABLED'}); eval {$conn->send_utf8($str);}; return; }
-----------------------------------------------------------------------------------
"push" event processing
-----------------------------------------------------------------------------------
elsif (($json_string->{'event'} eq "push") && ($usePushAPNSDirect || $usePushProxy)) {
sets the unread event count of events for a specific connection
the server keeps a tab of # of events it pushes out per connection
but won't know when the client has read them, so the client call tell
the server
using this message
if ($json_string->{'data'}->{'type'} eq "badge") { foreach (@activeconnections) { if ((exists $->{conn}) && ($->{conn}->ip() eq $conn->ip()) && ($->{conn}->port() eq $conn->port())) {
print "Badge match, setting to 0\n";
$_->{badge} = $json_string->{'data'}->{'badge'}; } } }
This sub type is when a device token is registered
if ($json_string->{'data'}->{'type'} eq "token") {
a token must have a platform otherwise I don't know whether to use
APNS or GCM if (!$json_string->{'data'}->{'platform'}) { my $str = encode_json({event=>'push', type=>'token',status=>'Fail', reason => 'MISSINGPLATFORM'}); eval {$conn->send_utf8($str);}; return; } foreach (@active_connections) {
this token already exists
if ($_->{token} eq $json_string->{'data'}->{'token'}) {
if the token doesn't belong to the same connection
then we have two connections owning the same token
so we need to delete the old one. This can happen when you load
the token from the persistent file and there is no connection
and then the client is loaded
if ( (!exists $->{conn}) || ($->{conn}->ip() ne $conn->ip() && $->{conn}->port() ne $conn->port())) { $->{pending} = INVALID_APNS; Info ("Duplicate token found, marking for deletion");
} else # token matches and connection matches, so it may be an update { $_->{token} = $jsonstring->{'data'}->{'token'}; $->{platform} = $jsonstring->{'data'}->{'platform'}; $->{monlist} = "-1"; $->{intlist} = "-1"; $->{pushstate} = $json_string->{'data'}->{'state'}; Info ("Storing token
...".substr($->{token},-10).",monlist:".$->{monlist}.",intlist:".$->{intlist}.",pushstate:".$->{pushstate}."\n");
my ($emonlist,$eintlist) = saveTokens($->{token}, $->{monlist}, $->{intlist}, $->{platform}, $->{pushstate}); $->{monlist} = $emonlist; $_->{intlist} = $eintlist; } }
The connection matches but the token does not
this can happen if this is the first token registration after push
notification registration
response is received
elsif ( (exists $->{conn}) && ($->{conn}->ip() eq $conn->ip()) && ($->{conn}->port() eq $conn->port())) { $->{token} = $jsonstring->{'data'}->{'token'}; $->{platform} = $jsonstring->{'data'}->{'platform'}; $->{monlist} = "-1"; $->{intlist} = "-1"; $->{pushstate} = $json_string->{'data'}->{'state'}; Info ("Storing token
...".substr($->{token},-10).",monlist:".$->{monlist}.",intlist:".$->{intlist}.",pushstate:".$->{pushstate}."\n");
my ($emonlist,$eintlist) = saveTokens($->{token}, $->{monlist}, $->{intlist}, $->{platform}, $->{pushstate}); $->{monlist} = $emonlist; $_->{intlist} = $eintlist;
} }
}
} # event = push
-----------------------------------------------------------------------------------
"control" event processing
-----------------------------------------------------------------------------------
elsif (($json_string->{'event'} eq "control") ) { if ($json_string->{'data'}->{'type'} eq "filter") { if (!$json_string->{'data'}->{'monlist'}) { my $str = encode_json({event=>'control', type=>'filter',status=>'Fail', reason => 'MISSINGMONITORLIST'}); eval {$conn->send_utf8($str);}; return; } if (!$json_string->{'data'}->{'intlist'}) { my $str = encode_json({event=>'control', type=>'filter',status=>'Fail', reason => 'MISSINGINTERVALLIST'}); eval {$conn->send_utf8($str);}; return; } my $monlist = $json_string->{'data'}->{'monlist'}; my $intlist = $json_string->{'data'}->{'intlist'};
print ("CONTROL GOT: $monlist and $intlist\n");
foreach (@activeconnections) { if ((exists $->{conn}) && ($->{conn}->ip() eq $conn->ip()) && ($->{conn}->port() eq $conn->port())) {
$->{monlist} = $monlist; $->{intlist} = $intlist; Info ("Contrl: Storing token
...".substr($->{token},-10).",monlist:".$->{monlist}.",intlist:".$->{intlist}.",pushstate:".$->{pushstate}."\n");
saveTokens($->{token}, $->{monlist}, $->{intlist}, $->{platform}, $_->{pushstate}); } } } if ($json_string->{'data'}->{'type'} eq "version") { foreach (@activeconnections) { if ((exists $->{conn}) && ($->{conn}->ip() eq $conn->ip()) && ($->{conn}->port() eq $conn->port())) { my $str = encode_json({event=>'control',type=>'version', status=>'Success', reason => '', version => $appversion}); eval {$->{conn}->send_utf8($str);};
} } }
} # event = control
-----------------------------------------------------------------------------------
"auth" event processing
-----------------------------------------------------------------------------------
This event type is when a command related to authorization is sent
elsif ($json_string->{'event'} eq "auth") { my $uname = $json_string->{'data'}->{'user'}; my $pwd = $json_string->{'data'}->{'password'};
return if ($uname eq "" || $pwd eq ""); foreach (@activeconnections) { if ( (exists $->{conn}) && ($->{conn}->ip() eq $conn->ip()) && ($->{conn}->port() eq $conn->port()) && ($_->{pending}==PENDING_WEBSOCKET)) { if (!validateZM($uname,$pwd)) {
bad username or password, so reject and mark for deletion
my $str = encodejson({event=>'auth', type=>'', status=>'Fail', reason => 'BADAUTH'}); eval {$->{conn}->sendutf8($str);}; Info("Bad authentication provided by ".$->{conn}->ip()); $_->{pending}=INVALID_WEBSOCKET; } else {
all good, connection auth was valid
$_->{pending}=VALIDWEBSOCKET; $->{token}=''; my $str = encode_json({event=>'auth', type=>'', status=>'Success', reason => '', version => $appversion}); eval {$->{conn}->sendutf8($str);}; Info("Correct authentication provided by ".$->{conn}->ip());
} } } } # event = auth else { my $str = encode_json({event=>$jsonstring->{'event'},type=>'', status=>'Fail', reason => 'NOTSUPPORTED'}); eval {$->{conn}->send_utf8($str);}; } }
This loads APNS tokens stored in a conf file
This ensures even if the daemon dies and
restarts APNS tokens are maintained
I also maintain monitor filter list
so that APNS notifications will only be pushed
for the monitors that are configured against
that token
sub loadTokens { return if (!$usePushAPNSDirect && !$usePushProxy); if ( ! -f PUSH_TOKEN_FILE) { open (my $foh, '>', PUSH_TOKEN_FILE); Info ("Creating ".PUSH_TOKEN_FILE); print $foh ""; close ($foh); }
open (my $fh, '<', PUSH_TOKEN_FILE); chomp( my @lines = <$fh>);
close ($fh); my @uniquetokens = uniq(@lines);
open ($fh, '>', PUSH_TOKEN_FILE);
This makes sure we rewrite the file with
unique tokens
foreach(@uniquetokens) { next if ($ eq ""); print $fh "$\n"; my ($token, $monlist, $intlist, $platform, $pushstate) = split (":",$_);
print "load: PUSHING $row\n";
push @active_connections, { token => $token, pending => VALID_WEBSOCKET, time=>time(), badge => 0, monlist => $monlist, intlist => $intlist, last_sent=>{}, platform => $platform, pushstate => $pushstate };
} close ($fh); }
This is called if the APNS feedback channel
reports an invalid token. We also remove it from
our token file
sub deleteToken { my $dtoken = shift; return if (!$usePushAPNSDirect && !$usePushProxy); return if ( ! -f PUSH_TOKEN_FILE);
open (my $fh, '<', PUSH_TOKEN_FILE); chomp( my @lines = <$fh>); close ($fh); my @uniquetokens = uniq(@lines);
open ($fh, '>', PUSH_TOKEN_FILE);
foreach(@uniquetokens) { my ($token, $monlist, $intlist, $platform, $pushstate) = split (":",$); next if ($ eq "" || $token eq $dtoken); print $fh "$_\n";
print "delete: $row\n";
push @active_connections, { token => $token, pending => VALID_WEBSOCKET, time=>time(), badge => 0, monlist => $monlist, intlist => $intlist, last_sent=>{}, platform => $platform, pushstate => $pushstate };
} close ($fh); }
When a client sends a token id,
I store it in the file
It can be sent multiple times, with or without
monitor list, so I retain the old monitor
list if its not supplied. In the case of zmNinja
tokens are sent without monitor list when the registration
id is received from apple, so we handle that situation
sub saveTokens { return if (!$usePushAPNSDirect && !$usePushProxy); my $stoken = shift; return if ($stoken eq ""); my $smonlist = shift; my $sintlist = shift; my $splatform = shift; my $spushstate = shift; return if ($stoken eq ""); open (my $fh, '<', PUSH_TOKEN_FILE) || Fatal ("Cannot open for read".PUSH_TOKEN_FILE); chomp( my @lines = <$fh>); close ($fh); my @uniquetokens = uniq(@lines); my $found = 0; open (my $fh, '>', PUSH_TOKEN_FILE) || Fatal ("Cannot open for write ".PUSH_TOKENFILE); foreach (@uniquetokens) { next if ($ eq ""); my ($token, $monlist, $intlist, $platform, $pushstate) = split (":",$_); if ($token eq $stoken) { $smonlist = $monlist if ($smonlist eq "-1"); $sintlist = $intlist if ($sintlist eq "-1"); $spushstate = $pushstate if ($spushstate eq ""); print $fh "$stoken:$smonlist:$sintlist:$splatform:$spushstate\n"; $found = 1; } else { $pushstate="enabled" if ($pushstate=""); print $fh "$token:$monlist:$intlist:$platform:$pushstate\n"; }
}
$smonlist = "" if ($smonlist eq "-1"); $sintlist = "" if ($sintlist eq "-1");
print $fh "$stoken:$smonlist:$sintlist:$splatform:$spushstate\n" if (!$found); close ($fh); registerOverPushProxy($stoken,$splatform) if ($usePushProxy);
print "Saved Token $token to file\n";
return ($smonlist, $sintlist);
}
This keeps the latest of any duplicate tokens
we need to ignore monitor list when we do this
sub uniq { my %seen; my @array = reverse @; # we want the latest my @farray=(); foreach (@array) { my ($token,$monlist,$intlist,$platform, $pushstate) = split (":",$);
not interested in monlist & intlist
if (! $seen{$token}++ ) { push @farray, "$token:$monlist:$intlist:$platform:$pushstate"; }
} return @farray;
}
Checks if the monitor for which
an alarm occurred is part of the monitor list
for that connection
sub getInterval { my $intlist = shift; my $monlist = shift; my $mid = shift;
print ("getInterval:MID:$mid INT:$intlist AND MON:$monlist\n");
my @ints = split (',',$intlist); my @mids = split (',',$monlist); my $idx = -1; foreach (@mids) { $idx++;
print ("Comparing $mid with $_\n");
if ($mid eq $_) { last; } }
print ("RETURNING index:$idx with Value:".$ints[$idx]."\n");
return $ints[$idx];
}
Checks if the monitor for which
an alarm occurred is part of the monitor list
for that connection
sub isInList { my $monlist = shift; my $mid = shift;
my @mids = split (',',$monlist); my $found = 0; foreach (@mids) { if ($mid eq $_) { $found = 1; last; } } return $found;
}
sub getIdentity { my $obj=shift; my $identity=""; if (exists $obj->{conn} ) { $identity = $obj->{conn}->ip().":".$obj->{conn}->port(); } if ($obj->{token}) { $identity=$identity." token ending in:...". substr($obj->{token},-10); } $identity="(unknown)" if (!$identity); return $identity; }
This is really the main module
It opens a WSS socket and keeps listening
sub initSocketServer { checkEvents(); testProxyURL() if ($usePushProxy);
my $ssl_server; if ($useSecure) { $ssl_server = IO::Socket::SSL->new( Listen => 10, LocalPort => EVENT_NOTIFICATION_PORT, Proto => 'tcp', Reuse => 1, SSL_cert_file => SSL_CERT_FILE, SSL_key_file => SSL_KEY_FILE ) or die "failed to listen: $!"; Info ("Secure WS(WSS) is enabled..."); } else { Info ("Secure WS is disabled..."); } Info ("Web Socket Event Server listening on port ".EVENT_NOTIFICATION_PORT."\n");
$wss = Net::WebSocket::Server->new( listen => $useSecure ? $ssl_server : EVENT_NOTIFICATION_PORT, tick_period => SLEEP_DELAY, on_tick => sub { checkConnection(); apnsFeedbackCheck() if ($usePushAPNSDirect); testProxyURL() if ($usePushProxy); my $ac = scalar @activeconnections; if (checkEvents()) { Info ("Broadcasting new events to all $ac websocket clients\n"); my ($serv) = @; my $i = 0; foreach (@active_connections) {
Let's see if this connection is interested in this alarm
my $monlist = $->{monlist}; my $intlist = $->{intlist}; my $lastsent = $->{lastsent}; my $obj = $; my $connid = getIdentity($obj); Info ("Checking alarm rules for $connid");
we need to create a per connection array which will be
a subset of main events with the ones that are not in its
monlist left out
my @localevents = (); foreach (@events) { if ($monlist eq "" || isInList($monlist, $->{MonitorId} ) ) { my $mint = getInterval($intlist, $monlist, $->{MonitorId}); my $elapsed; if ($lastsent->{$->{MonitorId}}) { $elapsed = time() - $lastsent->{$->{MonitorId}}; if ($elapsed >= $mint) { Info("Monitor ".$_->{MonitorId}." event: sending this out as $elapsed is
= interval of $mint"); push (@localevents, $_); $lastsent->{$->{MonitorId}} = time(); } else {
Info("Monitor ".$_->{MonitorId}." event: NOT sending this out as $elapsed is less than interval of $mint"); }
} else {
This means we have no record of sending any event to this monitor
$lastsent->{$->{MonitorId}} = time(); Info("Monitor ".$->{MonitorId}." event: last time not found, so sending"); push (@localevents, $); }
}
}
if this array is empty that means none of the alarms
were generated from a monitor it is interested in
next if (scalar @localevents == 0);
my $str = encode_json({event => 'alarm', type=>'', status=>'Success', events => \@localevents}); my $sup_str = encode_json({event => 'alarm', type=>'', status=>'Success', supplementary=>'true', events => \@localevents}); my %hash_str = (event => 'alarm', status=>'Success', events => \@localevents); $i++;
if there is APNS send it over APNS
if not, send it over Websockets
also disabled is a special state which means its registered over push
but it still wants messages over websockets - zmNinja sets this
when websockets override is enabled
if (($->{token} ne "") && ($->{pushstate} ne "disabled" )) { if ($usePushProxy) { Info ("Sending notification over PushProxy"); sendOverPushProxy($_,$alarm_header, $str) ; } elsif ($usePushAPNSDirect) {
Info ("Sending notification directly via APNS"); sendOverAPNS($_,$alarm_header, \%hash_str) ; }
send supplementary event data over websocket
if ($_->{pending} == VALIDWEBSOCKET) { if (exists $->{conn}) { Info ($->{conn}->ip()."-sending supplementary data over websockets\n"); eval {$->{conn}->send_utf8($sup_str);}; if ($@) {
$_->{pending} = INVALID_WEBSOCKET; } } }
}
if there is a websocket send it over websockets
elsif ($_->{pending} == VALIDWEBSOCKET) { if (exists $->{conn}) { Info ($->{conn}->ip()."-sending over websockets\n"); eval {$->{conn}->send_utf8($str);}; if ($@) {
$_->{pending} = INVALID_WEBSOCKET; } } }
}
} },
called when a new connection comes in
onconnect => sub { my ($serv, $conn) = @; my ($len) = scalar @activeconnections; Info ("got a websocket connection from ".$conn->ip()." (". $len.") active connections"); $conn->on( utf8 => sub { my ($conn, $msg) = @; checkMessage($conn, $msg); }, handshake => sub { my ($conn, $handshake) = @_; Info ("Websockets: New Connection Handshake requested from ".$conn->ip().":".$conn->port()." state=pending auth"); my $connect_time = time(); push @active_connections, {conn => $conn, pending => PENDING_WEBSOCKET, time=>$connect_time, monlist => "", intlist => "", lastsent=>{}, platform => "websocket", pushstate => '', badge => 0}; }, disconnect => sub { my ($conn, $code, $reason) = @; Info ("Websocket remotely disconnected from ".$conn->ip()); foreach (@activeconnections) { if ((exists $->{conn}) && ($->{conn}->ip() eq $conn->ip()) && ($->{conn}->port() eq $conn->port())) {
mark this for deletion only if device token
not present
if ( $->{token} eq '') { $->{pending}=INVALID_WEBSOCKET; Info( "Marking ".$conn->ip()." for deletion as websocket closed remotely\n"); } else {
Info( "NOT Marking ".$conn->ip()." for deletion as token ".$_->{token}." active\n"); } }
} }, );
} )->start; } Date/Time Component Server PID Level Message File Line 2016-02-19 09:57:28.939218 zma_m1 2523 INF Indkorsel: 11000 - Analysing at 5.52 fps zm_monitor.cpp 1287 2016-02-19 09:57:20.406754 zmc_m4 2546 INF Garage: 45000 - Capturing at 22.22 fps zm_monitor.cpp 3131 2016-02-19 09:57:14.835598 zma_m4 2551 INF Garage: 11000 - Analysing at 5.43 fps zm_monitor.cpp 1287 2016-02-19 09:57:07.038349 zmc_m1 2519 INF Indkorsel: 43000 - Capturing at 21.28 fps zm_monitor.cpp 3131 2016-02-19 09:56:52.731834 zmc_m3 5572 INF Terrasse: 9000 - Capturing at 18.52 fps zm_monitor.cpp 3131 2016-02-19 09:56:51.672354 web_js 2306 ERR Export request failed: 500 / Internal Server Error - exportFail() ?view=log 2016-02-19 09:56:44.126664 web_js 2746 ERR Export request failed: 500 / Internal Server Error - exportFail() ?view=log 2016-02-19 09:56:35.468823 zmc_m4 2546 INF Garage: 44000 - Capturing at 22.22 fps zm_monitor.cpp 3131 2016-02-19 09:56:20.552037 zmc_m1 2519 INF Indkorsel: 42000 - Capturing at 21.74 fps zm_monitor.cpp 3131 2016-02-19 09:55:58.650479 zmc_m3 5572 INF Terrasse: 8000 - Capturing at 18.18 fps zm_monitor.cpp 3131 2016-02-19 09:55:53.316918 zma_m3 2535 INF Terrasse: 11000 - Analysing at 5.26 fps zm_monitor.cpp 1287 2016-02-19 09:55:50.705142 zmc_m4 2546 INF Garage: 43000 - Capturing at 22.22 fps zm_monitor.cpp 3131 2016-02-19 09:55:34.230602 zmc_m1 2519 INF Indkorsel: 41000 - Capturing at 21.28 fps zm_monitor.cpp 3131 2016-02-19 09:55:08.066938 zma_m3 2535 INF Terrasse: 10742 - Closing event 241, alarm end zm_monitor.cpp 1707 2016-02-19 09:55:08.065944 zma_m3 2535 INF Terrasse: 10742 - Left alarm state (241) - 119(44) images zm_monitor.cpp 1702 2016-02-19 09:55:05.457454 zmc_m4 2546 INF Garage: 42000 - Capturing at 21.74 fps zm_monitor.cpp 3131 2016-02-19 09:55:03.230569 zmc_m3 5572 INF Terrasse: 7000 - Capturing at 17.54 fps zm_monitor.cpp 3131 2016-02-19 09:54:58.530678 zma_m3 2535 INF Terrasse: 10692 - Gone into alert state zm_monitor.cpp 1695 2016-02-19 09:54:50.296660 zmeventnotification 5723 INF 85.83.32.41-sending over websockets zmeventnotification.pl 2016-02-19 09:54:50.295950 zmeventnotification 5723 INF Monitor 3 event: last time not found, so sending zmeventnotification.pl 2016-02-19 09:54:50.295230 zmeventnotification 5723 INF Checking alarm rules for 85.83.32.41:54882 token ending in:...etePKHHtOi zmeventnotification.pl 2016-02-19 09:54:50.293980 zmeventnotification 5723 INF Broadcasting new events to all 1 websocket clients zmeventnotification.pl 2016-02-19 09:54:50.292810 zmeventnotification 5723 INF New event 241 reported for Terrasse zmeventnotification.pl 2016-02-19 09:54:49.792770 zma_m3 2535 INF Terrasse: 10657 - Opening new event 241, alarm start zm_monitor.cpp 1651 2016-02-19 09:54:49.791151 zma_m3 2535 INF Terrasse: 10657 - Gone into alarm state zm_monitor.cpp 1605 2016-02-19 09:54:47.921944 zma_m3 2535 INF Terrasse: 10648 - Gone into prealarm state zm_monitor.cpp 1680 2016-02-19 09:54:47.921293 zmc_m1 2519 INF Indkorsel: 40000 - Capturing at 21.74 fps zm_monitor.cpp 3131 2016-02-19 09:54:43.706839 zma_m3 2535 INF Terrasse: 10624 - Closing event 240, alarm end zm_monitor.cpp 1707 2016-02-19 09:54:43.705903 zma_m3 2535 INF Terrasse: 10624 - Left alarm state (240) - 153(76) images zm_monitor.cpp 1702 2016-02-19 09:54:41.103170 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:54:40.144580 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl 2016-02-19 09:54:40.142400 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:54:39.472330 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl 2016-02-19 09:54:39.470540 zmeventnotification 5723 INF Correct authentication provided by 85.83.32.41 zmeventnotification.pl 2016-02-19 09:54:39.314820 zmeventnotification 5723 INF Websockets: New Connection Handshake requested from 85.83.32.41:54882 state=pending auth zmeventnotification.pl 2016-02-19 09:54:39.312760 zmeventnotification 5723 INF got a websocket connection from 85.83.32.41 (0) active connections zmeventnotification.pl 2016-02-19 09:54:38.751025 web_php 3247 INF Login successful for user "admin" /usr/share/zoneminder/includes/functions.php 59 2016-02-19 09:54:34.232045 zma_m3 2535 INF Terrasse: 10574 - Gone into alert state zm_monitor.cpp 1695 2016-02-19 09:54:27.020207 zma_m1 2523 INF Indkorsel: 10000 - Analysing at 5.46 fps zm_monitor.cpp 1287 2016-02-19 09:54:24.564552 zma_m3 2535 INF Terrasse: 10532 - Gone back into alarm state zm_monitor.cpp 1686 2016-02-19 09:54:24.142570 zma_m3 2535 INF Terrasse: 10530 - Gone into alert state zm_monitor.cpp 1695 2016-02-19 09:54:20.297200 zmeventnotification 5723 INF 0.0.0.0-sending over websockets zmeventnotification.pl 2016-02-19 09:54:20.296610 zmeventnotification 5723 INF Monitor 3 event: sending this out as 25 is >= interval of 0 zmeventnotification.pl 2016-02-19 09:54:20.296020 zmeventnotification 5723 INF Checking alarm rules for 0.0.0.0:0 token ending in:...etePKHHtOi zmeventnotification.pl 2016-02-19 09:54:20.295100 zmeventnotification 5723 INF Broadcasting new events to all 1 websocket clients zmeventnotification.pl 2016-02-19 09:54:20.292820 zmeventnotification 5723 INF New event 240 reported for Terrasse zmeventnotification.pl 2016-02-19 09:54:19.981656 zmc_m4 2546 INF Garage: 41000 - Capturing at 22.22 fps zm_monitor.cpp 3131 2016-02-19 09:54:17.770862 zma_m3 2535 INF Terrasse: 10505 - Opening new event 240, alarm start zm_monitor.cpp 1651 2016-02-19 09:54:17.767855 zma_m3 2535 INF Terrasse: 10505 - Gone into alarm state zm_monitor.cpp 1605 2016-02-19 09:54:15.880628 zma_m3 2535 INF Terrasse: 10496 - Gone into prealarm state zm_monitor.cpp 1680 2016-02-19 09:54:13.939668 zma_m3 2535 INF Terrasse: 10485 - Closing event 239, alarm end zm_monitor.cpp 1707 2016-02-19 09:54:13.938607 zma_m3 2535 INF Terrasse: 10485 - Left alarm state (239) - 141(66) images zm_monitor.cpp 1702 2016-02-19 09:54:10.454276 zma_m4 2551 INF Garage: 10000 - Analysing at 5.43 fps zm_monitor.cpp 1287 2016-02-19 09:54:10.185100 zmeventnotification 5723 INF NOT Marking 85.83.32.41 for deletion as token
APA91bH7liIqurcA-dvZIVG4iEXVdrepcsjbQ9QbU80jccXnfLuvSeLgojX_6FWx5StndTOo-9Ar2BWzZylXHl2v0XAu7-rPOmJhFEXYYGLxQUIJic0K2sfIGRjd1jDsCJetePKHHtOi
active zmeventnotification.pl 2016-02-19 09:54:10.183890 zmeventnotification 5723 INF Websocket remotely disconnected from 85.83.32.41 zmeventnotification.pl 2016-02-19 09:54:06.674314 zmc_m3 5572 INF Terrasse: 6000 - Capturing at 18.52 fps zm_monitor.cpp 3131 2016-02-19 09:54:04.477639 zma_m3 2535 INF Terrasse: 10435 - Gone into alert state zm_monitor.cpp 1695 2016-02-19 09:54:01.463340 zmc_m1 2519 INF Indkorsel: 39000 - Capturing at 21.28 fps zm_monitor.cpp 3131 2016-02-19 09:53:55.296030 zmeventnotification 5723 INF 85.83.32.41-sending over websockets zmeventnotification.pl 2016-02-19 09:53:55.295380 zmeventnotification 5723 INF Monitor 3 event: last time not found, so sending zmeventnotification.pl 2016-02-19 09:53:55.294420 zmeventnotification 5723 INF Checking alarm rules for 85.83.32.41:54809 token ending in:...etePKHHtOi zmeventnotification.pl 2016-02-19 09:53:55.293050 zmeventnotification 5723 INF Broadcasting new events to all 1 websocket clients zmeventnotification.pl 2016-02-19 09:53:55.291570 zmeventnotification 5723 INF New event 239 reported for Terrasse zmeventnotification.pl 2016-02-19 09:53:50.612716 zma_m3 2535 INF Terrasse: 10378 - Opening new event 239, alarm start zm_monitor.cpp 1651 2016-02-19 09:53:50.611010 zma_m3 2535 INF Terrasse: 10378 - Gone into alarm state zm_monitor.cpp 1605 2016-02-19 09:53:48.749175 zma_m3 2535 INF Terrasse: 10369 - Gone into prealarm state zm_monitor.cpp 1680 2016-02-19 09:53:45.380270 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:53:44.800960 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl 2016-02-19 09:53:44.799090 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:53:44.214010 zmeventnotification 5723 INF Contrl: Storing token ...etePKHHtOi,monlist:1,3,4,intlist:0,0,0,pushstate:disabled zmeventnotification.pl 2016-02-19 09:53:40.373090 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:53:39.785070 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl 2016-02-19 09:53:39.783310 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:53:39.202220 zmeventnotification 5723 INF Contrl: Storing token ...etePKHHtOi,monlist:1,3,4,intlist:0,0,0,pushstate:disabled zmeventnotification.pl 2016-02-19 09:53:37.655280 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:53:37.074020 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl 2016-02-19 09:53:37.072400 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:53:36.501696 web_php 2837 INF Login successful for user "admin" /usr/share/zoneminder/includes/functions.php 59 2016-02-19 09:53:36.483200 zmeventnotification 5723 INF Contrl: Storing token ...etePKHHtOi,monlist:1,3,4,intlist:0,0,0,pushstate:disabled zmeventnotification.pl 2016-02-19 09:53:34.961523 zmc_m4 2546 INF Garage: 40000 - Capturing at 22.73 fps zm_monitor.cpp 3131 2016-02-19 09:53:34.911170 zmeventnotification 5723 INF Pushproxy registration success zmeventnotification.pl 2016-02-19 09:53:34.327790 zmeventnotification 5723 INF Storing token ...etePKHHtOi,monlist:-1,intlist:-1,pushstate:disabled zmeventnotification.pl 2016-02-19 09:53:34.327150 zmeventnotification 5723 INF Duplicate token found, marking for deletion zmeventnotification.pl 2016-02-19 09:53:34.325960 zmeventnotification 5723 INF Correct authentication provided by 85.83.32.41 zmeventnotification.pl 2016-02-19 09:53:34.314460 zmeventnotification 5723 INF Websockets: New Connection Handshake requested from 85.83.32.41:54809 state=pending auth zmeventnotification.pl 2016-02-19 09:53:34.312020 zmeventnotification 5723 INF got a websocket connection from 85.83.32.41 (1) active connections zmeventnotification.pl 2016-02-19 09:53:23.227340 zmaudit 2584 INF deleting zmaudit.pl 2016-02-19 09:53:23.223190 zmaudit 2584 INF Filesystem event '3/210' does not exist in database zmaudit.pl 2016-02-19 09:53:20.286600 zmeventnotification 5723 INF Web Socket Event Server listening on port 9000 zmeventnotification.pl 2016-02-19 09:53:20.285630 zmeventnotification 5723 INF Secure WS is disabled... zmeventnotification.pl 2016-02-19 09:53:20.284290 zmeventnotification 5723 INF PushProxy https://pliablepixels.ddns.net:8801 is reachable. zmeventnotification.pl 2016-02-19 09:53:18.865110 zmeventnotification 5723 INF Checking https://pliablepixels.ddns.net:8801 reachability... zmeventnotification.pl 2016-02-19 09:53:18.863270 zmeventnotification 5723 INF Loading monitors zmeventnotification.pl 2016-02-19 09:53:18.862720 zmeventnotification 5723 INF Reloading Monitors... zmeventnotification.pl 2016-02-19 09:53:18.861590 zmeventnotification 5723 INF Total event client connections: 1 zmeventnotification.pl 2016-02-19 09:53:18.860470 zmeventnotification 5723 INF Event Notification daemon v 0.7 starting zmeventnotification.pl 2016-02-19 09:53:18.859290 zmeventnotification 5723 INF direct APNS disabled zmeventnotification.pl 2016-02-19 09:53:18.857950 zmeventnotification 5723 INF Push enabled via PushProxy zmeventnotification.pl 2016-02-19 09:53:18.509960 zmdc 5723 INF 'zmeventnotification.pl' started at 16/02/19 09:53:18 zmdc.pl 2016-02-19 09:53:18.509870 zmdc 2477 INF 'zmeventnotification.pl' starting at 16/02/19 09:53:18, pid = 5723 zmdc.pl Feb 19, 2016 9:55:10 AM DEBUG dis-alarming monitor ID=1 Feb 19, 2016 9:54:50 AM DEBUG App is in foreground, displaying banner Feb 19, 2016 9:54:50 AM DEBUG Scheduled a 10 sec timer for dis-alarming monitor ID=1 Feb 19, 2016 9:54:50 AM INFO Real-time event:
{"status":"Success","type":"","events":[{"EventId":"241","Name":"Terrasse","MonitorId":"3"}],"event":"alarm"}
Feb 19, 2016 9:54:40 AM INFO Stream authentication construction: &auth=d4a07aed227e2b63538e27d50e7226c4 Feb 19, 2016 9:54:40 AM INFO DataModel: Extracted a stream authentication key of: d4a07aed227e2b63538e27d50e7226c4 Feb 19, 2016 9:54:40 AM INFO Real-time event:
{"version":"0.7","reason":"","event":"auth","status":"Success","type":""} Feb 19, 2016 9:54:39 AM DEBUG DataModel: Getting auth from http://
/index.php?view=watch&mid=1&connkey=199031 with mid=1 Feb 19, 2016 9:54:39 AM INFO Stored montage order does not exist Feb 19, 2016 9:54:39 AM INFO Inside Montage Ctrl:We found 3 monitors Feb 19, 2016 9:54:39 AM INFO Stored montage order does not exist Feb 19, 2016 9:54:39 AM INFO ConnKey for 4 is :44553 Feb 19, 2016 9:54:39 AM INFO ConnKey for 3 is :38858 Feb 19, 2016 9:54:39 AM INFO ConnKey for 1 is :199031 Feb 19, 2016 9:54:39 AM INFO Monitor load was successful, loaded 3 monitors Feb 19, 2016 9:54:39 AM INFO ZM_EVENT_IMAGE_DIGITS is 5 Feb 19, 2016 9:54:39 AM INFO Got API version: TKK 1.29.0 Feb 19, 2016 9:54:39 AM DEBUG openHandShake: state of push is disabled Feb 19, 2016 9:54:39 AM INFO openHandshake: Websocket open Feb 19, 2016 9:54:39 AM INFO ZM has recaptcha disabled - good Feb 19, 2016 9:54:39 AM INFO Stream authentication construction: Feb 19, 2016 9:54:39 AM INFO getMonitors:Loading all monitors Feb 19, 2016 9:54:39 AM DEBUG getAPIversion called Feb 19, 2016 9:54:39 AM DEBUG Feb 19, 2016 9:54:39 AM DEBUG Config URL for digits is:http:// /api/configs/viewByName/ZM_EVENT_IMAGE_DIGITS.json Feb 19, 2016 9:54:39 AM DEBUG PortalLogin: auth success Feb 19, 2016 9:54:39 AM INFO Deferring auth key, as monitorId unknown Feb 19, 2016 9:54:39 AM DEBUG auth-success emit:Successful Feb 19, 2016 9:54:39 AM INFO zmAutologin successfully logged into Zoneminder Feb 19, 2016 9:54:39 AM INFO Websocket was closed, trying to re-open Feb 19, 2016 9:54:39 AM DEBUG Push Notification registration ID received: {"registrationId":"APA91bH7liIqurcA-dvZIVG4iEXVdrepcsjbQ9QbU80jccXnfLuvSeLgojX_6FWx5StndTOo-9Ar2BWzZylXHl2v0XAu7-rPOmJhFEXYYGLxQUIJic0K2sfIGRjd1jDsCJetePKHHtOi"}
Feb 19, 2016 9:54:38 AM INFO Checking if reCaptcha is enabled in ZM... Feb 19, 2016 9:54:38 AM INFO zmAutologin called Feb 19, 2016 9:54:38 AM INFO Cancelling zmAutologin timer Feb 19, 2016 9:54:38 AM DEBUG PIN code not set Feb 19, 2016 9:54:38 AM INFO not checking for touchID Feb 19, 2016 9:54:38 AM INFO User credentials are provided Feb 19, 2016 9:54:38 AM DEBUG Inside Portal login Enter handler Feb 19, 2016 9:54:38 AM INFO zmNinja Version: 1.0.7 Feb 19, 2016 9:54:37 AM INFO Setting up POST LOGIN timer Feb 19, 2016 9:54:37 AM INFO Deleting old log file as it exceeds 20000 bytes 2016-02-19T08:54:37.895Z INFO Checked for update 0 hours ago. Not checking again 2016-02-19T08:54:37.873Z INFO Initializing Websocket with URL ws://
, will connect later... 2016-02-19T08:54:37.866Z INFO Setting up push registration 2016-02-19T08:54:37.864Z DEBUG loginData structure values: {"serverName":"DELLABLO","username":"admin","password":"
","url":"http:// ","apiurl":"http:// /api","eventServer":"ws:// ","maxMontage":"10","streamingurl":"http:// ","maxFPS":"3","montageQuality":"50","singleImageQuality":"50","useSSL":false,"keepAwake":true,"isUseAuth":"1","isUseEventServer":true,"disablePush":true,"eventServerMonitors":"1,3,4","eventServerInterval":"0,0,0","refreshSec":"2","enableDebug":true,"usePin":false,"pinCode":"","canSwipeMonitors":true,"persistMontageOrder":false,"onTapScreen":"events","enableh264":true,"gapless":true,"montageOrder":"","montageHiddenOrder":"","montageArraySize":"1:1:1","graphSize":200,"minAlarmCount":"0","montageSize":"1","useNphZms":true,"packMontage":true,"forceNetworkStop":false,"defaultPushSound":false,"exitOnSleep":false,"enableBlog":true} > 2016-02-19T08:54:37.863Z INFO DataModel init recovered this loginData > as {"serverName":"DELLABLO","username":"admin","password":" ","url":"http:// > ","apiurl":"http:// /api","eventServer":"ws:// ","maxMontage":"10","streamingurl":"http:// ","maxFPS":"3","montageQuality":"50","singleImageQuality":"50","useSSL":false,"keepAwake":true,"isUseAuth":"1","isUseEventServer":true,"disablePush":true,"eventServerMonitors":"1,3,4","eventServerInterval":"0,0,0","refreshSec":"2","enableDebug":true,"usePin":false,"pinCode":"","canSwipeMonitors":true,"persistMontageOrder":false,"onTapScreen":"events","enableh264":true,"gapless":true,"montageOrder":"","montageHiddenOrder":"","montageArraySize":"1:1:1","graphSize":200,"minAlarmCount":"0","montageSize":"1","useNphZms":true,"packMontage":true,"forceNetworkStop":false,"defaultPushSound":false,"exitOnSleep":false,"enableBlog":true} > 2016-02-19T08:54:37.860Z INFO ZMData init: checking for stored > variables > & > setting up log file > 2016-02-19T08:54:37.859Z INFO You are running on android > 2016-02-19T08:54:37.854Z INFO Device is ready > > — > Reply to this email directly or view it on GitHub > < https://github.com/pliablepixels/zmNinja/issues/135#issuecomment-186131986 > . — Reply to this email directly or view it on GitHub < https://github.com/pliablepixels/zmNinja/issues/135#issuecomment-186260657 . — Reply to this email directly or view it on GitHub https://github.com/pliablepixels/zmNinja/issues/135#issuecomment-186684284 .
Hi all. I recently installed a new ZM and ZmN + the event sever. Its not my first ZM by fare, but its the first with the API and the event server from pliablepixels.
One thing is still unresolved.
The APP and notification system is super cool, but on my system I don't always hear the alerts, or notice them afterwards. Am I the only one?
I don't get any alert icon in the android status bar, and the sound volume on the notification sound is extremely low. so low i have to activity pay attention when i test to notice it. All volumes are on full! I must say, the chosen blub.mp3 does not make much of an alarm sound to begin with though.
I suggest that the "ZmN Bell icon" is shown in the status bar on alerts, until they are "cleared" and that the APP will have options to customize the sound, with option to chose an mp3 and volume to achieve the following.
1: The notification will be loud and clear even if the phone is a bit away or in a pocket. 2: If you do not hear the sound, but later have a look at the phone, you should be alerted with a status bar ZmN icon.
Thanks for a cool APP and event server. It has made my Zoneminder a lot more fun ;O) I'm running a HTC M7, rooted, S-OFF with ViperOne 9.0.0 ◦ Sense 7