POPUP_ON_ALARM for Chrome and SOUND_ON_ALARM for montage view

If you've made a patch to quick fix a bug or to add a new feature not yet in the main tree then post it here so others can try it out.
Post Reply
kennbr34
Posts: 48
Joined: Sat Jan 14, 2017 6:43 pm

POPUP_ON_ALARM for Chrome and SOUND_ON_ALARM for montage view

Post by kennbr34 »

Introductary Banter

Hi everybody. So neither of these features were working properly for me. Well, SOUND_ON_ALARM was, but not for montage view. The pop-up on alarm never worked for me either. I spent the last couple of days figure out how to hack them into shape. I don't really know JavaScript or PHP, and I can barely code in C so I realize that these solutions may not be the most graceful, but I noticed a lot of other people have had problems with these for a while too. So if no one else is going to take a whack at it...

Platform Specifications
Zone Minder Machine:
ZoneMinder 1.30.4-xenial1 from Ubuntu repositories
Ubuntu 16.04

Browsing Machine:
Ubuntu 16.04 and Chrome Version 65.0.3325.181.

Patched Against:
1.30.4

What this fixes

So just to clarify, and to bring any pertinent web searches here... Currently the WEB_POPUP_ON_ALARM and WEB_SOUND_ON_ALARM options in zoneminder are not working correctly, or to the satisfaction of most users. WEB_POPUP_ON_ALARM didn't work for any modern browser I tried on Linux. This option says "Should the monitor window jump to the top if an alarm occurs". With these modifications, it will instead open a new window with a list of events from all monitors that occurred in the last minute.

Secondly, the WEB_SOUND_ON_ALARM option does not work in montage view. This option states "Should the monitor window play a sound if an alarm occurs". There seems to be a difference in how supported this across a wide array of browsers, so all I can confirm is that this works on the specified build of Chrome.

Files and Directories

On my Ubuntu installation, the root ZoneMinder directory is at "/usr/share/zoneminder/"

From there, all the files we're going to be working on will be in "/www/skins/classic/views/"

All paths mentioned from here on will be relative to that directory unless otherwise specified.

Files to be edited:

js/watch.js
js/montage.js
js/montage.js.php
watch.php
montage.php

Be sure to apply it with --dry-run first and backup your originals just to be safe. If this doesn't work, I'm including failsafe instructions for manually editing.

Patching with 'diff'

SOUND_ON_ALARM for montage plus POPUP_ON_ALARM for chrome diff...

Code: Select all

diff -Naur ./views/js/montage.js ./views_patched/js/montage.js
--- ./views/js/montage.js	2018-04-15 01:28:35.364986299 -0700
+++ ./views_patched/js/montage.js	2018-04-15 02:54:13.708110910 -0700
@@ -69,24 +69,38 @@
 
             if ( newAlarm )
             {
-                if ( false && SOUND_ON_ALARM )
+                if ( SOUND_ON_ALARM )
                 {
-                    // Enable the alarm sound
-                    $('alarmSound').removeClass( 'hidden' );
-                }
+			// Enable the alarm sound
+            		if ( !canPlayPauseAudio )
+                		$('alarmSound').removeClass( 'hidden' );
+            		else
+               			 $('MediaPlayer').Play();                
+		}
                 if ( POPUP_ON_ALARM )
                 {
-                    windowToFront();
+			if (navigator.userAgent.indexOf('Chrome/') > 0) {
+    				if (window.detwin) {
+        				window.detwin.close();
+        				window.detwin = null;
+    				}
+			}
+			window.detwin = window.open('?view=events&page=1&filter[terms][0][attr]=DateTime&filter[terms][0][op]=%3E%3D&filter[terms][0][val]=-1+minute',  'ALARM RAISED', '...');
+			window.detwin.focus();
+                //windowToFront();
                 }
             }
-            if ( false && SOUND_ON_ALARM )
-            {
-                if ( oldAlarm )
+                if ( SOUND_ON_ALARM )
                 {
-                    // Disable alarm sound
-                    $('alarmSound').addClass( 'hidden' );
+            	    if ( oldAlarm )
+                    {
+		    // Disable alarm sound
+	            if ( !canPlayPauseAudio )
+        	    $('alarmSound').addClass( 'hidden' );
+                    else
+                    $('MediaPlayer').Stop();
                 }
-            }
+	    }
         }
         else
         {
diff -Naur ./views/js/montage.js.php ./views_patched/js/montage.js.php
--- ./views/js/montage.js.php	2018-04-15 01:29:10.581874297 -0700
+++ ./views_patched/js/montage.js.php	2018-04-15 01:33:34.088661273 -0700
@@ -26,6 +26,8 @@
 
 var canStreamNative = <?php echo canStreamNative()?'true':'false' ?>;
 
+var canPlayPauseAudio = Browser.ie;
+
 var monitorData = new Array();
 <?php
 foreach ( $monitors as $monitor )
diff -Naur ./views/js/watch.js ./views_patched/js/watch.js
--- ./views/js/watch.js	2018-04-15 01:29:47.650388424 -0700
+++ ./views_patched/js/watch.js	2018-04-15 01:57:51.010680595 -0700
@@ -83,8 +83,15 @@
         }
         if ( POPUP_ON_ALARM )
         {
-            window.focus();
-        }
+		if (navigator.userAgent.indexOf('Chrome/') > 0) {
+    			if (window.detwin) {
+        			window.detwin.close();
+        			window.detwin = null;
+    			}
+		}
+		window.detwin = window.open('?view=events&page=1&filter[terms][0][attr]=DateTime&filter[terms][0][op]=%3E%3D&filter[terms][0][val]=-1+minute',  'ALARM RAISED', '...');
+		window.detwin.focus();
+	}
     }
     if ( SOUND_ON_ALARM )
     {
diff -Naur ./views/montage.php ./views_patched/montage.php
--- ./views/montage.php	2018-04-15 01:27:10.174190704 -0700
+++ ./views_patched/montage.php	2018-04-15 01:33:04.766582472 -0700
@@ -84,6 +84,7 @@
 
 xhtmlHeaders(__FILE__, translate('Montage') );
 ?>
+<meta http-equiv="refresh" content="60" >
 <body>
   <div id="page">
     <div id="header">
@@ -152,5 +153,50 @@
       </div>
     </div>
   </div>
+    </div>
+  </div>
+
+<?php
+if ( ZM_WEB_SOUND_ON_ALARM )
+{
+    $soundSrc = ZM_DIR_SOUNDS.'/'.ZM_WEB_ALARM_SOUND;
+?>
+      <div id="alarmSound" class="hidden">
+<?php
+    if ( ZM_WEB_USE_OBJECT_TAGS && isWindows() )
+    {
+?>
+        <object id="MediaPlayer" width="0" height="0"
+          classid="CLSID:22D6F312-B0F6-11D0-94AB-0080C74C7E95"
+          codebase="http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=6,0,02,902">
+          <param name="FileName" value="<?php echo $soundSrc ?>"/>
+          <param name="autoStart" value="0"/>
+          <param name="loop" value="1"/>
+          <param name="hidden" value="1"/>
+          <param name="showControls" value="0"/>
+          <embed src="<?php echo $soundSrc ?>"
+            autostart="true"
+            loop="true"
+            hidden="true">
+          </embed>
+        </object>
+<?php
+    }
+    else
+    {
+?>
+        <embed src="<?php echo $soundSrc ?>"
+          autostart="true"
+          loop="true"
+          hidden="true">
+        </embed>
+<?php
+    }
+?>
+      </div>
+<?php
+}
+?>
+
 </body>
 </html>
diff -Naur ./views/watch.php ./views_patched/watch.php
--- ./views/watch.php	2018-04-15 01:30:35.896645337 -0700
+++ ./views_patched/watch.php	2018-04-15 01:32:55.608431844 -0700
@@ -77,6 +77,7 @@
 
 xhtmlHeaders( __FILE__, $monitor->Name()." - ".translate('Feed') );
 ?>
+<meta http-equiv="refresh" content="60" >
 <body>
   <div id="page">
     <div id="content">
@@ -240,5 +241,6 @@
 ?>
     </div>
   </div>
+
 </body>
 </html>
POPUP_ON_ALARM for Chrome diff

Code: Select all

diff -Naur '--exclude=*montage*' ./views/js/watch.js ./views_patched/js/watch.js
--- ./views/js/watch.js	2018-04-15 01:29:47.650388424 -0700
+++ ./views_patched/js/watch.js	2018-04-15 01:57:51.010680595 -0700
@@ -83,8 +83,15 @@
         }
         if ( POPUP_ON_ALARM )
         {
-            window.focus();
-        }
+		if (navigator.userAgent.indexOf('Chrome/') > 0) {
+    			if (window.detwin) {
+        			window.detwin.close();
+        			window.detwin = null;
+    			}
+		}
+		window.detwin = window.open('?view=events&page=1&filter[terms][0][attr]=DateTime&filter[terms][0][op]=%3E%3D&filter[terms][0][val]=-1+minute',  'ALARM RAISED', '...');
+		window.detwin.focus();
+	}
     }
     if ( SOUND_ON_ALARM )
     {
diff -Naur '--exclude=*montage*' ./views/watch.php ./views_patched/watch.php
--- ./views/watch.php	2018-04-15 01:30:35.896645337 -0700
+++ ./views_patched/watch.php	2018-04-15 01:32:55.608431844 -0700
@@ -77,6 +77,7 @@
 
 xhtmlHeaders( __FILE__, $monitor->Name()." - ".translate('Feed') );
 ?>
+<meta http-equiv="refresh" content="60" >
 <body>
   <div id="page">
     <div id="content">
@@ -240,5 +241,6 @@
 ?>
     </div>
   </div>
+
 </body>
 </html>
SOUND_ON_ALARM for montage view diff...

Code: Select all

diff -Naur '--exclude=*watch*' ./views/js/montage.js ./views_patched/js/montage.js
--- ./views/js/montage.js	2018-04-15 01:28:35.364986299 -0700
+++ ./views_patched/js/montage.js	2018-04-15 02:01:00.268530676 -0700
@@ -69,24 +69,30 @@
 
             if ( newAlarm )
             {
-                if ( false && SOUND_ON_ALARM )
+                if ( SOUND_ON_ALARM )
                 {
-                    // Enable the alarm sound
-                    $('alarmSound').removeClass( 'hidden' );
-                }
+			// Enable the alarm sound
+            		if ( !canPlayPauseAudio )
+                		$('alarmSound').removeClass( 'hidden' );
+            		else
+               			 $('MediaPlayer').Play();                
+		}
                 if ( POPUP_ON_ALARM )
                 {
-                    windowToFront();
+                    	windowToFront();
                 }
             }
-            if ( false && SOUND_ON_ALARM )
-            {
-                if ( oldAlarm )
+                if ( SOUND_ON_ALARM )
                 {
-                    // Disable alarm sound
-                    $('alarmSound').addClass( 'hidden' );
+            	    if ( oldAlarm )
+                    {
+		    // Disable alarm sound
+	            if ( !canPlayPauseAudio )
+        	    $('alarmSound').addClass( 'hidden' );
+                    else
+                    $('MediaPlayer').Stop();
                 }
-            }
+	    }
         }
         else
         {
diff -Naur '--exclude=*watch*' ./views/js/montage.js.php ./views_patched/js/montage.js.php
--- ./views/js/montage.js.php	2018-04-15 01:29:10.581874297 -0700
+++ ./views_patched/js/montage.js.php	2018-04-15 01:33:34.088661273 -0700
@@ -26,6 +26,8 @@
 
 var canStreamNative = <?php echo canStreamNative()?'true':'false' ?>;
 
+var canPlayPauseAudio = Browser.ie;
+
 var monitorData = new Array();
 <?php
 foreach ( $monitors as $monitor )
diff -Naur '--exclude=*watch*' ./views/montage.php ./views_patched/montage.php
--- ./views/montage.php	2018-04-15 01:27:10.174190704 -0700
+++ ./views_patched/montage.php	2018-04-15 01:33:04.766582472 -0700
@@ -84,6 +84,7 @@
 
 xhtmlHeaders(__FILE__, translate('Montage') );
 ?>
+<meta http-equiv="refresh" content="60" >
 <body>
   <div id="page">
     <div id="header">
@@ -152,5 +153,50 @@
       </div>
     </div>
   </div>
+    </div>
+  </div>
+
+<?php
+if ( ZM_WEB_SOUND_ON_ALARM )
+{
+    $soundSrc = ZM_DIR_SOUNDS.'/'.ZM_WEB_ALARM_SOUND;
+?>
+      <div id="alarmSound" class="hidden">
+<?php
+    if ( ZM_WEB_USE_OBJECT_TAGS && isWindows() )
+    {
+?>
+        <object id="MediaPlayer" width="0" height="0"
+          classid="CLSID:22D6F312-B0F6-11D0-94AB-0080C74C7E95"
+          codebase="http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=6,0,02,902">
+          <param name="FileName" value="<?php echo $soundSrc ?>"/>
+          <param name="autoStart" value="0"/>
+          <param name="loop" value="1"/>
+          <param name="hidden" value="1"/>
+          <param name="showControls" value="0"/>
+          <embed src="<?php echo $soundSrc ?>"
+            autostart="true"
+            loop="true"
+            hidden="true">
+          </embed>
+        </object>
+<?php
+    }
+    else
+    {
+?>
+        <embed src="<?php echo $soundSrc ?>"
+          autostart="true"
+          loop="true"
+          hidden="true">
+        </embed>
+<?php
+    }
+?>
+      </div>
+<?php
+}
+?>
+
 </body>
 </html>
POPUP_ON_ALARM for Chrome: https://pastebin.com/NYVgLFvG
SOUND_ON_ALARM in Montage View: https://pastebin.com/r2sDzfX5
Both: https://pastebin.com/ypuzdSGk

To apply, copy and paste the contents of the pastebin into a text file, and save it as "views.patch" into your "...zoneminder/www/skins/classic/" directory.

Then run

(Note do this as root or with sudo since you won't have write permissions otherwise)

Code: Select all

patch --dry-run -p1 < views.patch
If all is successful, it should output something like this...

Code: Select all

checking file views/js/montage.js
checking file views/js/montage.js.php
checking file views/js/watch.js
checking file views/montage.php
checking file views/watch.php
Then run it with backups created, just to be on the safe side.

Code: Select all

patch -b -p1 < ./views.patch
Output if all goes well...

Code: Select all

patching file views/js/montage.js
patching file views/js/montage.js.php
patching file views/js/watch.js
patching file views/montage.php
patching file views/watch.php
If something went wrong with the patch, the original unmodified version will be saved with the ".orig" suffix.

Test it out, see if it works for you. If not, you can reverse the patch.

Code: Select all

patch -R -p1 < ./views.patch
If it Didn't Work

If patching went successful, but things aren't working right...


I made this patch against the 1.30.4 branch from git since I had already modified my files from the Ubuntu repositories, and I didn't want to risk changing something else I didn't intend to by doing a reinstall from packages. So if you have trouble with the patch working against your current installation, try again but applying it against 1.30.4 branch from the git repositories, and then move the "view" directory from there into your package manager's zoneminder installation.

Otherwise, carry on with manual editing.

Manually Editing Instructions

Here's the meat and potatoes of it in case patching doesn't work and you need to edit the files manually. Let's start with the SOUND_ON_ALARM for montage view as this seems to be the most desired thing for most, and requires the most modification.

It is best to create backups before beginning this editing, in case you place something in the wrong spot, or things have changed in the code dramatically since this post.

(Edit these as root or you won't have write permissions)

Open "js/montage.js" in a text editor, and search for "if (newAlarm)" (or go to line 70). It should look like this...

Code: Select all

if ( newAlarm )
            {
                if ( false && SOUND_ON_ALARM )
                {
                    // Enable the alarm sound
                    $('alarmSound').removeClass( 'hidden' );
                }
                if ( POPUP_ON_ALARM )
                {
                    windowToFront();
                }
            }
            if ( false && SOUND_ON_ALARM )
            {
                if ( oldAlarm )
                {
                    // Disable alarm sound
                    $('alarmSound').addClass( 'hidden' );
                }
            }
Basically you want to change your logic decisions from

Code: Select all

if ( false && SOUND_ON_ALARM )
to

Code: Select all

if ( SOUND_ON_ALARM)
Then change each Disable and Enable alarm sound code block as so...

Code: Select all

// Enable the alarm sound
            		if ( !canPlayPauseAudio )
                		$('alarmSound').removeClass( 'hidden' );
            		else
               			 $('MediaPlayer').Play();

Code: Select all

// Disable alarm sound
	            if ( !canPlayPauseAudio )
        	         $('alarmSound').addClass( 'hidden' );
                    else
                         $('MediaPlayer').Stop();
Now you need to edit "montage.php"

Scroll to the bottom and between the very last div tag, and the closing body tag (beginning after line 156), add this

Code: Select all

<?php
if ( ZM_WEB_SOUND_ON_ALARM )
{
    $soundSrc = ZM_DIR_SOUNDS.'/'.ZM_WEB_ALARM_SOUND;
?>
      <div id="alarmSound" class="hidden">
<?php
    if ( ZM_WEB_USE_OBJECT_TAGS && isWindows() )
    {
?>
        <object id="MediaPlayer" width="0" height="0"
          classid="CLSID:22D6F312-B0F6-11D0-94AB-0080C74C7E95"
          codebase="http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=6,0,02,902">
          <param name="FileName" value="<?php echo $soundSrc ?>"/>
          <param name="autoStart" value="0"/>
          <param name="loop" value="1"/>
          <param name="hidden" value="1"/>
          <param name="showControls" value="0"/>
          <embed src="<?php echo $soundSrc ?>"
            autostart="true"
            loop="true"
            hidden="true">
          </embed>
        </object>
<?php
    }
    else
    {
?>
        <embed src="<?php echo $soundSrc ?>"
          autostart="true"
          loop="true"
          hidden="true">
        </embed>
<?php
    }
?>
      </div>
<?php
}
?>
Then open "js/montage.js.php" and at line 5 there should be

Code: Select all

var canStreamNative = <?php echo canStreamNative()?'true':'false' ?>;
After this line, make a new line and paste

Code: Select all

var canPlayPauseAudio = Browser.ie;
Now your montage view should play the same sound your watch view will. I tried to keep as much of the OS specific code logic in tact, but I have not tested this on IE.

Now for the simpler part, the POPUP_ON_ALARM. The problem with this is both the montage and watch view are using outdated and unsupported methods to bring the window to focus. I'll start with the watch view. Open "js/watch.js"

Search for "if ( POPUP_ON_ALARM )" or go to line 84.

Code: Select all

if ( POPUP_ON_ALARM )
        {
            window.focus();
        }
Unfortunately not a lot of browsers support this method. What I did was found a solution on overstack that will basically create a new window, closing the old one and updating it for each new alarm. The URL it brings up shows all events for all monitors in the last 1 minute. It can be tweaked acccording to the filter settings, or any URL can be entered, I just wasn't familiar enough with zoneminder to make it bring up any specific monitor URL. This code ends up working better for montage view anyway, as it won't be bringing up a specific player.

So replace the pop-up method ( it's different in "js/montage.js" ) with this code block....

Code: Select all

if ( POPUP_ON_ALARM )
        {
		if (navigator.userAgent.indexOf('Chrome/') > 0) {
    			if (window.detwin) {
        			window.detwin.close();
        			window.detwin = null;
    			}
		}
		window.detwin = window.open('?view=events&page=1&filter[terms][0][attr]=DateTime&filter[terms][0][op]=%3E%3D&filter[terms][0][val]=-1+minute',  'ALRM RAISED', '...');
		window.detwin.focus();
	}
Now when motion triggers an alarm, there will be a list of events within the last minute from all monitors that will popup in a new window, and this window will refresh with each successive event.

Auto-Refresh

For whatever reason, the modifications to play sound require a page refresh if the POPUP_ON_ALARM option using the new code is on. The simplest thing to do is to edit either/both 'watch.php' and 'montage.php' with a simple meta tag right before the first body tag like so...

Code: Select all

<meta http-equiv="refresh" content="60" >
<body>
This will refresh the page every 60 seconds and make sure that SOUND_ON_ALARM still functions with thew new POPUP_ON_ALARM code.

Conclusion

So just to wrap up, I didn't intend to make this into a large production or something like that, and was just intending to show how I did a simple fix, but the modifications are a little more extensive than I first thought once I got to writing out how to do it. Just hoping this helps someone down the line, perhaps ( and probably ) a better coder than I who will do a good enough job to get these fixes committed to the main branch.
Post Reply