Haiku How to run a shell script from HH

2MuchTech

Member
I have what I thought would be a simple task, but I'm running into problems.  I have a shell script on my iMac and it interacts with the Synology Surveillance Station API.  If I run the scripts from a Terminal command line everything works fine.  I need to run these scripts from within Haiku Helper (triggered by events on my OmniPro II).  I didn't see anything in the documentation about directly running shell scripts, but I did see that HH can run an Apple Script, and Apple Script has a "do shell script" command.
 
Unfortunately when I run my Apple Script from within the script editor for testing, I can see that it's executing my script (various tmp files that I expect to find are getting created, so I know it's running), but somewhere inside the shell script it's failing, because I see no effect in Surveillance Station like I do when I run the script from the Terminal command line.  The Result pane in the Script Editor isn't capturing any error information, so I can't tell what's failing.
 
Is there a better or more direct way to run a shell script from HH?
 
My shell scripts are basically running wget to send Surveillance Station the commands I want, so I suppose I could potentially use XMLHttpRequest() in HH to talk to the Surveillance Station API, but their API relies on cookies to communicate between HTTP requests, and I'm not sure how HH will handle that.  Using wget I can stash the cookies from one call and load them again for the next call.
 
Since I hadn't heard any suggestions back yet, I went ahead and tried calling the Surveillance Station API directly from HH using XMLHttpRequest. While the calls appear to work, Surveillance Station returns an error on the step that's supposed to make a change to the system. The sequence of events to accomplish my task is 1) call API to log in, 2) call API to adjust settings on Surveillance Station system, 3) call API to log out. Using wget I can save a copy of the cookies generated in step #1, and then reload them for the subsequent two calls to the API. The session ID gets passed along to the subsequent two calls via this mechanism, so the API knows the second step has been authorized.

When I used HH's XMLHttpRequest to call the API, the login step returns success, but then step #2 returns an error indicating failure due to unauthorized access, so apparently the session info from step #1 isn't getting preserved.

Any ideas how to address this? I don't know much about using GET and POST (I tried both in HH's XMLHttpRequest, BTW), so I'm hoping there's a simple solution to what appears to be an issue with cookies not being retained between XMLHttpRequest calls.
 
Well, I would still like to know how to call a shell script from HH, but I've managed to get around the issue by using a variant of the Surveillance Station API call that returns the session ID in the result, so instead of relying on cookies (which, based on my problems getting it to work with cookies, I'm assuming HH doesn't support) to pass the session ID back to the subsequent API calls, I can now pass it in as a command line parameter.  The code seems to work well enough using this method, although I've found that sometimes the Surveillance Station API call returns a "fail" code for no apparent reason.  Fortunately a few resends of the same command seems to eventually get it to work, so I've just coded a retry operation into my HH script and it seems reliable now.
 
There is not a way to do this currently due to sandboxing. The best you can do is HTTP request to a script that is served via HTTP.
 
Well, despite my initial frustrations trying to get it to work via a shell script, I ultimately was able to get it working pretty well using direct HHTP calls to the third party API. The HH scripting has actually turned out to provide some pretty nice functionality that I didn't have before. I'm still having some issues with email notifications being unreliable, and I suspect it has something to do with a problem in HH, but the automation aspects of the scripting work well. I have plans to further extend the functionality of the scripting in the coming weeks. My next goal is to have motion detector events from my OmniPro trigger PTZ events on my Surveillance Station cameras so they focus their attention on the area with motion detected. Based on my current experience with HH scripting, I don't expect to run into any major roadblocks. I'm hoping all of this scripting work doesn't have to be completely reworked for the Space version of the Helper app, but right now I'm still using Haiku almost exclusively anyway, so as long as it continues to be nominally supported I'm happy.
 
Hi
 
I want to integrate my home automation with Synology to, but I'm not that good at scripting, is there any change you would share our script?
 
Sorry about the formatting, but this should help you figure out how to control your Synology Surveillance Station.
 

var numEnableAttempts = 0; // used to let us retry a Camera Enable a few times in case it's failing on initial attempt
var camTimeoutID = 0; // stores the timerTimeout ID for the 3 hour auto-resume of camera recording
var DiskStation_Addr = '192.168.x.x:5000'; // where to find the Synology Disk Station on our network
var SS_Admin_Password = 'Put_Password_Here'; // Surveillance Station password - must match the password DiskStation has for userID "admin"


 

// This function gets called internally when the setTimeout timer expires. We fire off the setTimeout timer whenever the Suspend Cams button processing
// occurs. This allows us to resume the camera recording at a fixed time after the user suspends it (default is 3 hours). We also use the setTimeout
// timer (set at 5 seconds) to handle retries of our Resume operation when the initial attempt fails.
function resumeCameras() {
if (numEnableAttempts == 0) { // if we're not here because the 5 second retry time expired...
helper.log(controller,'Cameras being resumed via 3 hour Suspend timer expiring');
}
camTimeoutID = 0; // reset - we're no longer within the 3 hour timeout window
controller.buttonWithName('Resume Cams').activate();
}


 
 
Code:
function onButtonActivate(button) {
	switch (button.name) {
		case 'Suspend Cams':				// User is asking to suspend back yard camera recording for 3 hours,
			if (camTimeoutID != 0) {		// check to see if we're already suspended
				helper.log(controller,"Suspend Cameras requested, but they're already suspended.");
				break;	// break out of the switch since we're not going to do anything, otherwise fall thru to the Resume Cams case
			}

		case 'Resume Cams':	// or they're asking to resume them now (or the 3 hour timer expired and an auto-resume is in progress)
			if (button.name == 'Suspend Cams') {
				var op = 'Disable'
			} else {
				var op = 'Enable'
			}
			// This code is designed to work with Surveillance Station 7.x (ver 2.0 of the API) Jan 2016
			var cameraIDs = '14,15,19,20';	// List of camera ID #s to suspend recording
							// 14=Patios / 15=Back Yard West / 19=Back Yard PTZ / 20=MBR PTZ
			var sid = '';			// Holds the SID we get back from the Surveillance Station login call
			var sid_pos = '';		// Holds the position of the SID in the GET response
			var req = new XMLHttpRequest();
	
			req.onload = xmlLoaded1;
			req.open('GET','http://' + DiskStation_Addr + '/webapi/auth.cgi?api=SYNO.API.Auth&method=Login&version=2&account=admin&passwd=' + SS_Admin_Password + '&Session=SurveillanceStation&format=sid');
			req.send(); 
	
			function xmlLoaded1() {		// called when Login GET returns
				helper.log(controller,'Login: ' + req.responseText);
				sid_pos = req.responseText.indexOf('"sid":')
				if (sid_pos > -1) {	// Should be true unless login failed
					sid = req.responseText.slice(sid_pos + 7); // copy everything starting 7 chars after the sid label
					sid = sid.slice(0,sid.indexOf('"')); // copy everything up to the trailing " after the sid
					helper.log(controller,'sid="' + sid + '"');
					req.onload = xmlLoaded2; 
					req.open('GET','http://' + DiskStation_Addr + '/webapi/entry.cgi?api=SYNO.SurveillanceStation.Camera&method=' + op + '&version=3&cameraIds="' + cameraIDs +'"&_sid=' + sid);
					req.send();
				} else {
					helper.sendNotification(controller,'Failed to log in to Surveillance Station.  Camera recording not suspended.');
				}
  			}  // xmlLoaded1()

			function xmlLoaded2() {		// called when Disable/Enable GET returns
				if (req.responseText.indexOf('"success":true') > -1) {
					numEnableAttempts = 0;	// reset our retry counter every time we succeed
					helper.log(controller,op + ': Success'); // a success response can be quite lengthy, so just cut to the chase
					if (op == 'Disable') {
						controller.messageWithName('Cams Suspended').log();
						helper.sendNotification(controller,'Back yard camera recording is being suspended for 3 hours.');
						camTimeoutID = setTimeout(resumeCameras, 3*60*60*1000); // set the timer for 3 hours that will re-enable them
					} else {
						helper.sendNotification(controller,'Back yard camera recording resumed.');
						controller.messageWithName('Cams Resumed').log();
						clearTimeout(camTimeoutID);	// clear the 3 hour timeout timer in case this Resume was from a manual user request
						camTimeoutID = 0;	// reset - we're no longer within the 3 hour timeout window
					}
				} else {	// for some reason the attempt failed…
					helper.log(controller,op + ': ' + req.responseText);
					if (op == 'Enable') {	// if this is an attempt to Enable, it's important to try again a couple of times
						if (numEnableAttempts < 3) { //  because we don't want to leave the cameras disabled
							numEnableAttempts += 1;
							helper.log(controller, 'Back yard camera recording Resume operation failed.  Trying again…');
							setTimeout(resumeCameras, 5000); // wait 5 seconds and try the Surveillance Station API commands again
						} else {	// Even after 3 attempts we still failed to resume recording
							controller.messageWithName('Cam Resume Fail').log();
							helper.log(controller, 'Back yard camera recording Resume operation FAILED.  No retries left to try again.'); 
							helper.sendNotification(controller,'Back yard camera recording FAILED to resume!');
						}	
					}
				}
				req.onload = xmlLoaded3; 
				req.open('GET','http://' + DiskStation_Addr + '/webapi/auth.cgi?api=SYNO.API.Auth&method=Logout&version=2&_sid=' + sid);
				req.send();
  			}  // xmlLoaded2()

			function xmlLoaded3() {		// called when Logout GET returns
				helper.log(controller,'Logout: ' + req.responseText);
			}  // xmlLoaded3()
		
			break;  // done with Suspend Cams / Resume Cams
			
		}  // switch
		
}  // onButtonActivate()
 
Do you have any scripts that you could share?
 
Using Haiku Helper and cannot find the API document anymore.
 
Thanks for any help!
 
rlust said:
Do you have any scripts that you could share?
 
Using Haiku Helper and cannot find the API document anymore.
 
Thanks for any help!
Document is still there:  HaikuHelper > Help > Script API Documentation
 
Back
Top