UNPKG

goproh4

Version:

API for controlling GoPro Hero 4 cameras

478 lines (423 loc) 15.1 kB
'use strict'; var GoPro = require('./constant.js'); var when = require('when'); var request = require('request'); var dgram = require('dgram'); var fs = require("fs"); var mac = require('mac-address'); GoPro = GoPro || {}; GoPro.maxRetries = 20; GoPro.retryTimeout = 100; GoPro.busyError = 'Camera is busy'; GoPro.keepAlivePeriod = 2500; GoPro.keepAliveMessage = new Buffer("_GPHD_:0:0:2:0.000000\n"); GoPro.keepAlivePort = 8554; GoPro.Camera = function (options) { options = options || {}; this._ip = options.ip || '10.5.5.9'; this._broadcastip = options.broadcastip || '10.5.5.255'; this._status = {}; this._mac = validateMacAddress(options.mac); this._ready = false; if (!this._mac) { retrieveMacAddress(this); } else { this._ready = true; } }; function validateMacAddress(macAddress) { try { if (macAddress instanceof Buffer) { if (macAddress.length === mac.LENGTH) { return macAddress; } else { return null; } } if (macAddress instanceof Array) { if (macAddress.length === mac.LENGTH) { return Buffer.from(macAddress); } else { return null; } } return mac.toBuffer(macAddress); } catch (e) { return null; } } function retrieveMacAddress(cam) { request('http://' + cam._ip + '/gp/gpControl', function (error, response, body) { if (!error && response.statusCode == 200) { try { var infos = JSON.parse(body); cam._mac = validateMacAddress(infos.info.ap_mac.replace(/(..)(..)(..)(..)(..)(..)/, '$1:$2:$3:$4:$5:$6')); cam._ready = true; if (cam._readyResolved) { cam._readyResolved(cam); } return; } catch (e) { error = e; } } if (cam._readyRejected) { cam._readyRejected(error); } }); } GoPro.Camera.prototype.ready = function () { var self = this; return when.promise(function (resolve, reject) { if (self._ready === true) { return resolve(); } self._readyResolved = resolve; self._readyRejected = reject; }); }; GoPro.Camera.prototype._retryPromise = function (action, maxRetries) { var retries = 0; maxRetries = maxRetries || GoPro.maxRetries; return when.promise(function func(resolve, reject, notify) { (function f() { when.promise(action) .then(function (data) { resolve(data); }, function (error) { if (++retries < maxRetries) { return f(); } reject(error); }) })(); }); }; GoPro.Camera.prototype.status = function (id) { var self = this; return this._retryPromise(function (resolve, reject) { var url = 'http://' + self._ip + '/gp/gpControl/status'; request.get(url, function (error, response, body) { if (!error && response.statusCode == 200) { try { var data = JSON.parse(body); if (id !== undefined) { return resolve(data.status[id]); } return resolve(data); } catch (e) { return reject(e); } } if (!error && response.statusCode == 500) { return reject(GoPro.busyError); } return reject(error); }); }); }; GoPro.Camera.prototype.mode = function (mode, submode) { var self = this; return this._retryPromise(function (resolve, reject) { if (mode !== undefined && submode !== undefined) { var url = 'http://' + self._ip + '/gp/gpControl/command/sub_mode?mode=' + mode + '&sub_mode=' + submode; request.get(url, function (error, response, body) { if (!error && response.statusCode == 200) { return resolve(); } if (!error && response.statusCode == 500) { return reject(GoPro.busyError); } return reject(error); }); } else { var url = 'http://' + self._ip + '/gp/gpControl/command/mode?p=' + mode; request.get(url, function (error, response, body) { if (!error && response.statusCode == 200) { return resolve(); } if (!error && response.statusCode == 500) { return reject(GoPro.busyError); } return reject(error); }); } }); }; GoPro.Camera.prototype.start = function () { var self = this; return this._retryPromise(function (resolve, reject) { var url = 'http://' + self._ip + '/gp/gpControl/command/shutter?p=1'; request.get(url, function (error, response, body) { if (!error && response.statusCode == 200) { return resolve(); } if (!error && response.statusCode == 500) { return reject(GoPro.busyError); } return reject(error); }); }); }; GoPro.Camera.prototype.stop = function () { var self = this; return this._retryPromise(function (resolve, reject) { var url = 'http://' + self._ip + '/gp/gpControl/command/shutter?p=0'; request.get(url, function (error, response, body) { if (!error && response.statusCode == 200) { return resolve(); } if (!error && response.statusCode == 500) { return reject(GoPro.busyError); } return reject(error); }); }); }; GoPro.Camera.prototype._keepAliveStart = function () { var self = this; if (this._keepAliveInterval) { this._keepAliveStop(); } var kaInterval = function () { var client = dgram.createSocket("udp4"); client.send(GoPro.keepAliveMessage, 0, GoPro.keepAliveMessage.length, GoPro.keepAlivePort, self._ip, function () { client.close(); }); }; this._keepAliveInterval = setInterval(kaInterval, GoPro.keepAlivePeriod); kaInterval(); }; GoPro.Camera.prototype._keepAliveStop = function () { clearInterval(this._keepAliveInterval); }; GoPro.Camera.prototype.startStream = function () { var self = this; return this._retryPromise(function (resolve, reject) { var url = 'http://' + self._ip + '/gp/gpControl/execute?p1=gpStream&c1=start'; request.get(url, function (error, response, body) { if (!error && response.statusCode == 200) { self._keepAliveStart(); return resolve(); } if (!error && response.statusCode == 500) { return reject(GoPro.busyError); } return reject(error); }); }); }; GoPro.Camera.prototype.restartStream = function () { var self = this; return this._retryPromise(function (resolve, reject) { var url = 'http://' + self._ip + '/gp/gpControl/execute?p1=gpStream&c1=restart'; request.get(url, function (error, response, body) { if (!error && response.statusCode == 200) { self._keepAliveStart(); return resolve(); } if (!error && response.statusCode == 500) { return reject(GoPro.busyError); } return reject(error); }); }); }; GoPro.Camera.prototype.stopStream = function () { var self = this; return this._retryPromise(function (resolve, reject) { var url = 'http://' + self._ip + '/gp/gpControl/execute?p1=gpStream&c1=stop'; request.get(url, function (error, response, body) { if (!error && response.statusCode == 200) { self._keepAliveStop(); return resolve(); } if (!error && response.statusCode == 500) { return reject(GoPro.busyError); } return reject(error); }); }); }; GoPro.Camera.prototype.set = function (setting_id, setting_value) { var self = this; return this._retryPromise(function (resolve, reject) { if (setting_id !== undefined && setting_value !== undefined) { var url = 'http://' + self._ip + '/gp/gpControl/setting/' + setting_id + '/' + setting_value; return request.get(url, function (error, response, body) { if (!error && response.statusCode == 200) { return resolve(); } if (!error && response.statusCode == 500) { return reject(GoPro.maxRetries); } return reject(error); }); } if (setting_id !== undefined) { var url = 'http://' + self._ip + '/gp/gpControl/status'; return request.get(url, function (error, response, body) { if (!error && response.statusCode == 200) { try { var data = JSON.parse(body); return resolve(data.settings[setting_id]); } catch (e) { return reject(e); } } if (!error && response.statusCode == 500) { return reject(GoPro.busyError); } return reject(error); }); } reject('Invalid parameters'); }); }; GoPro.Camera.prototype.powerOn = function () { if (this._mac === undefined) { return console.error('[failure] MAC address unknown'); } var message = new Buffer(102); message[0] = 0xff; message[1] = 0xff; message[2] = 0xff; message[3] = 0xff; message[4] = 0xff; message[5] = 0xff; for (var j = 0; j < 16; ++j) { var c = 6 + j * this._mac.length; for (var i = 0; i < this._mac.length; ++i) { message[c + i] = this._mac[i]; } } var client = dgram.createSocket("udp4"); var c = 0; for (var i=0;i < 10; ++i) { client.send(message, 0, message.length, 9, this._ip, function () { ++c; if (c !== 10) return; client.close(); }); } }; GoPro.Camera.prototype.powerOff = function () { var self = this; return this._retryPromise(function (resolve, reject) { var url = 'http://' + self._ip + '/gp/gpControl/command/system/sleep'; request.get(url, function (error, response, body) { if (!error && response.statusCode == 200) { return resolve(); } if (!error && response.statusCode == 500) { return reject(GoPro.busyError); } return reject(error); }); }); }; GoPro.Camera.prototype.listMedia = function () { var self = this; return this._retryPromise(function (resolve, reject) { var url = 'http://' + self._ip + '/gp/gpMediaList'; request.get(url, function (error, response, body) { if (!error && response.statusCode == 200) { try { return resolve(JSON.parse(body)); } catch (e) { return reject(e); } } if (!error && response.statusCode == 500) { return reject(GoPro.busyError); } return reject(error); }); }); }; GoPro.Camera.prototype.getMediaStream = function (directory, filename) { var url = 'http://' + this._ip + '/videos/DCIM/' + directory + '/' + filename; return when.promise(function (resolve) { resolve(request.get(url)); }); }; GoPro.Camera.prototype.getMedia = function (directory, filename, path) { var self = this; return when.promise(function (resolve, reject) { self.getMediaStream(directory, filename).then(function (stream) { stream.on('error', function (err) { reject(err); }) .on('response', function(response) { if (response.statusCode !== 200) { reject(); } else { var out = fs.createWriteStream(path); out.on('error', function (err) { reject(err); }); out.on('finish', function () { resolve(path); }); stream.pipe(out); } }); }); }); }; GoPro.Camera.prototype.deleteAll = function () { var self = this; return this._retryPromise(function (resolve, reject) { var url = 'http://' + self._ip + '/gp/gpControl/command/storage/delete/all'; request.get(url, function (error, response, body) { if (!error && response.statusCode == 200) { try { return resolve(JSON.parse(body)); } catch (e) { return reject(e); } } if (!error && response.statusCode == 500) { return reject(GoPro.busyError); } return reject(error); }); }); }; GoPro.Camera.prototype.deleteLast = function () { var self = this; return this._retryPromise(function (resolve, reject) { var url = 'http://' + self._ip + '/gp/gpControl/command/storage/delete/last'; request.get(url, function (error, response, body) { if (!error && response.statusCode == 200) { try { return resolve(JSON.parse(body)); } catch (e) { return reject(e); } } if (!error && response.statusCode == 500) { return reject(GoPro.busyError); } return reject(error); }); }); }; GoPro.Camera.prototype.videoInfo = function (video_path) { var self = this; return this._retryPromise(function (resolve, reject) { var url = 'http://' + self._ip + '/gp/gpMediaMetadata?p=' + video_path + '&t=videoinfo'; request.get(url, function (error, response, body) { if (!error && response.statusCode == 200) { try { return resolve(JSON.parse(body)); } catch (e) { return reject(e); } } if (!error && response.statusCode == 500) { return reject(GoPro.busyError); } reject(error); }); }); }; module.exports = GoPro;