diff --git a/plugins/google-device-access/package-lock.json b/plugins/google-device-access/package-lock.json index 357e8443b..a8128030d 100644 --- a/plugins/google-device-access/package-lock.json +++ b/plugins/google-device-access/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/google-device-access", - "version": "0.0.73", + "version": "0.0.75", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/google-device-access", - "version": "0.0.73", + "version": "0.0.75", "dependencies": { "@googleapis/smartdevicemanagement": "^0.2.0", "axios": "^0.21.1", diff --git a/plugins/google-device-access/package.json b/plugins/google-device-access/package.json index 5f2875aba..7dbb42b24 100644 --- a/plugins/google-device-access/package.json +++ b/plugins/google-device-access/package.json @@ -45,5 +45,5 @@ "@types/node": "^14.17.11", "@types/url-parse": "^1.4.3" }, - "version": "0.0.73" + "version": "0.0.75" } diff --git a/plugins/google-device-access/src/main.ts b/plugins/google-device-access/src/main.ts index 5cd73376c..349c609a7 100644 --- a/plugins/google-device-access/src/main.ts +++ b/plugins/google-device-access/src/main.ts @@ -90,6 +90,8 @@ function toNestMode(mode: ThermostatMode): string { class NestCamera extends ScryptedDeviceBase implements Camera, VideoCamera, MotionSensor, BinarySensor, BufferConverter, ObjectDetector { signalingMime: string; + lastMotionEventId: string; + lastImage: Promise; constructor(public provider: GoogleSmartDeviceAccess, public device: any) { super(device.name.split('/').pop()); @@ -116,29 +118,39 @@ class NestCamera extends ScryptedDeviceBase implements Camera, VideoCamera, Moti } async takePicture(options?: PictureOptions): Promise { - const hasEventImages = !!this.device?.traits?.['sdm.devices.traits.CameraEventImage']; - const lastMotionEventId = this.storage.getItem('lastMotionEventId'); - if (!lastMotionEventId || !hasEventImages) { - const realDevice = systemManager.getDeviceById(this.id); - try { - const msos = await realDevice.getVideoStreamOptions(); - const prebuffered = msos.find(mso => mso.prebuffer); - if (prebuffered) - return realDevice.getVideoStream(prebuffered); - } - catch (e) { - } - return mediaManager.createMediaObject(black, 'image/jpeg'); + // if this stream is prebuffered, its safe to use the prebuffer to generate an image + const realDevice = systemManager.getDeviceById(this.id); + try { + const msos = await realDevice.getVideoStreamOptions(); + const prebuffered = msos.find(mso => mso.prebuffer); + if (prebuffered) + return realDevice.getVideoStream(prebuffered); + } + catch (e) { } - const result = await this.provider.authPost(`/devices/${this.nativeId}:executeCommand`, { - command: "sdm.devices.commands.CameraEventImage.GenerateImage", - params: { - eventId: lastMotionEventId, - }, - }); + // try to fetch the latest event image if one is queued + const hasEventImages = !!this.device?.traits?.['sdm.devices.traits.CameraEventImage']; + if (hasEventImages && this.lastMotionEventId) { + const eventId = this.lastMotionEventId; + this.lastMotionEventId = undefined; + const result = this.provider.authPost(`/devices/${this.nativeId}:executeCommand`, { + command: "sdm.devices.commands.CameraEventImage.GenerateImage", + params: { + eventId, + }, + }).then(response => response.data); + this.lastImage = result; + } - return mediaManager.createMediaObject(result.data, 'image/jpeg'); + // use the last event image + if (this.lastImage) { + const data = await this.lastImage; + return mediaManager.createMediaObject(data, 'image/jpeg'); + } + + // send "no snapshot available" image + return mediaManager.createMediaObject(black, 'image/jpeg'); } async getPictureOptions(): Promise { @@ -558,9 +570,14 @@ class GoogleSmartDeviceAccess extends ScryptedDeviceBase implements OauthClient, const camera: NestCamera = this.devices.get(nativeId) as any; if (camera) { camera.motionDetected = true; - camera.storage.setItem('lastMotionEventId', - events['sdm.devices.events.CameraMotion.Motion']?.eventId - || events['sdm.devices.events.CameraPerson.Person']?.eventId); + const eventId = events['sdm.devices.events.CameraMotion.Motion']?.eventId + || events['sdm.devices.events.CameraPerson.Person']?.eventId; + camera.lastMotionEventId = eventId; + // images expire in 30 seconds after publish + setTimeout(() => { + if (camera.lastMotionEventId === eventId) + camera.lastMotionEventId = undefined; + }, 30); setTimeout(() => camera.motionDetected = false, 30000); if (events['sdm.devices.events.CameraPerson.Person']) { this.onDeviceEvent(ScryptedInterface.ObjectDetection, {