From 079957fa755777485be1a45c5fd34690c52e0884 Mon Sep 17 00:00:00 2001 From: Brandon McFarlin Date: Fri, 21 Feb 2020 13:19:45 -0500 Subject: [PATCH] fix for thumbnails and camera state --- index.js | 11 ++++++++++- lib/nest.js | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/nestcam.js | 43 +++++++++++++++++++++++++++++++++---------- package.json | 2 +- 4 files changed, 94 insertions(+), 12 deletions(-) diff --git a/index.js b/index.js index 8ad702c..0981e40 100755 --- a/index.js +++ b/index.js @@ -4,6 +4,8 @@ let Accessory, Service, Characteristic, hap, UUIDGen; const Nest = require('./lib/nest.js').NestAPI; const NestConnection = require('./lib/nest-connection.js'); +const UPDATE_INTERVAL = 10000; + Promise.delay = function(time_ms) { return new Promise(resolve => setTimeout(resolve, time_ms)); } @@ -101,7 +103,7 @@ class NestCamPlatform { accessory.addService(motion); setInterval(async function() { camera.checkMotion(accessory); - }, 10000); + }, UPDATE_INTERVAL); } //Add enabled/disabled service accessory.addService(Service.Switch, 'Streaming') @@ -112,6 +114,13 @@ class NestCamPlatform { self.log.info("Setting %s to %s", accessory.displayName, (value ? 'on' : 'off')); callback(); }); + //Check enabled/disabled state + setInterval(async function() { + await camera.updateInfo(); + let service = accessory.getService(Service.Switch); + service.updateCharacteristic(Characteristic.On, camera.enabled); + }, UPDATE_INTERVAL); + accessory.configureCameraSource(camera); configuredAccessories.push(accessory); }); diff --git a/lib/nest.js b/lib/nest.js index 9c8648c..57516f5 100644 --- a/lib/nest.js +++ b/lib/nest.js @@ -1,5 +1,6 @@ 'use strict'; +const https = require('https'); const axios = require('axios'); const EventEmitter = require('events'); const NestCam = require('./nestcam').NestCam; @@ -64,6 +65,55 @@ class NestAPI extends EventEmitter { self.log.debug(req.method + ' request sent to ' + req.url); return (await axios(req)).data; } + + /** + * Send a generic api request using promises + * @param hostname The base uri to send the request + * @param endpoint The endpoint to send the request + * @param method Usually "GET" or "POST" + * @param body The body of the request or null if a "GET" + */ + sendPromiseRequest(hostname, endpoint, method, body) { + let self = this; + + return new Promise((resolve, reject) => { + let headers = { + 'User-Agent': 'iPhone iPhone OS 11.0 Dropcam/5.14.0 com.nestlabs.jasper.release Darwin', + 'Referer': 'https://home.nest.com/' + }; + + if (method === 'POST') { + headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'; + } + + if (self.accessToken !== undefined) { + headers['Cookie'] = 'user_token=' + self.accessToken; + } + + let options = { + hostname: hostname, + path: endpoint, + method: method, + headers: headers + }; + let req = https.request(options, (res) => { + if (res.statusCode < 200 || res.statusCode >= 300) { + let error = new Error('Unexpected API Error - ' + res.statusCode); + error.code = res.statusCode; + reject(error); + } + + const resBody = []; + res.on('data', (chunk) => resBody.push(chunk)); + res.on('end', () => resolve(Buffer.concat(resBody))); + }); + req.on('error', (err) => reject(err)); + if (body !== undefined) { + req.write(body); + } + req.end(); + }); + } } module.exports = { diff --git a/lib/nestcam.js b/lib/nestcam.js index a61b805..b0993a4 100644 --- a/lib/nestcam.js +++ b/lib/nestcam.js @@ -26,6 +26,31 @@ class NestCam { self.motionDetected = false; } + async updateInfo() { + let self = this; + let query = querystring.stringify({ + 'uuid': self.uuid + }); + try { + self.log.debug("Updating info for %s", self.name); + let response = await self.api.sendRequest(NestAPIHostname, '/api/cameras.get_with_properties?' + query, 'GET'); + response.items.forEach((info) => { + self.name = info.name; + self.uuid = info.uuid; + self.enabled = info.is_streaming_enabled; + self.serialNumber = info.serial_number; + self.softwareVersion = info.combined_software_version; + self.detectors = info.detectors; + self.type = info.type; + self.nexusTalkHost = info.direct_nexustalk_host; + self.apiHost = info.nexus_api_http_server; + }); + } catch(error) { + error.status = error.response && error.response.status; + self.log.error('Error fetching cameras - ' + error.status); + } + } + async toggleActive(enabled) { let self = this; let query = querystring.stringify({ @@ -151,21 +176,19 @@ class NestCam { // Camera Source - async handleSnapshotRequest(request, callback) { + handleSnapshotRequest(request, callback) { let self = this; let query = querystring.stringify({ uuid: self.uuid, width: request.width }); - try { - let response = await self.api.sendRequest(self.apiHost, '/get_image?' + query, 'GET'); - callback(undefined, response); - } catch(error) { - error.status = error.response && error.response.status; - if (error.status != 404) { - self.log.error('Error getting image - ' + error.status); - } - } + self.api.sendPromiseRequest(self.apiHost, '/get_image?' + query, 'GET') + .then((response) => { + callback(undefined, response); + }) + .catch((err) => { + callback(err); + }); } handleCloseConnection(connectionID) { diff --git a/package.json b/package.json index fffad6d..f14d8d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "homebridge-nest-cam2", - "version": "1.1.0", + "version": "1.1.1", "description": "Nest cam plugin for homebridge: https://homebridge.io/", "license": "ISC", "keywords": [