Skip to content

Commit

Permalink
fix(homebridge): handle missing snapshots and offline cameras
Browse files Browse the repository at this point in the history
  • Loading branch information
dgreif committed Aug 7, 2019
1 parent 746bb5d commit adc08f6
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 10 deletions.
19 changes: 12 additions & 7 deletions api/rest-client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import axios, { AxiosRequestConfig, ResponseType } from 'axios'
import { delay, generateRandomId, logError, logInfo } from './util'
import { delay, generateRandomId, logError, logInfo, stringify } from './util'
import * as querystring from 'querystring'
import { AuthTokenResponse, SessionResponse } from './ring-types'

Expand Down Expand Up @@ -184,7 +184,7 @@ export class RingRestClient {
waitSeconds = isNaN(retryAfter) ? 200 : Number.parseInt(retryAfter)

logError(
`Session response rate limited. Waiting to retry for ${waitSeconds} seconds`
`Session response rate limited. Waiting to retry after ${waitSeconds} seconds`
)
await delay((waitSeconds + 1) * 1000)

Expand Down Expand Up @@ -265,11 +265,16 @@ export class RingRestClient {
}

if (response.status === 404 && url.startsWith(clientApiBaseUrl)) {
logError(
'Session hardware_id not found. Creating a new session and trying again.'
)
this.refreshSession()
return this.request(options)
logError('404 from endpoint ' + url)
if (response.data === '') {
logError(
'Session hardware_id not found. Creating a new session and trying again.'
)
this.refreshSession()
return this.request(options)
}

throw new Error('Not found with response: ' + stringify(response.data))
}

logError(`Request to ${url} failed`)
Expand Down
8 changes: 8 additions & 0 deletions api/ring-camera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,14 @@ export class RingCamera {
return getBatteryLevel(this.data)
}

get hasLowBattery() {
return this.data.alerts.battery === 'low'
}

get isOffline() {
return this.data.alerts.connection === 'offline'
}

doorbotUrl(path: string) {
return clientApi(`doorbots/${this.id}/${path}`)
}
Expand Down
12 changes: 12 additions & 0 deletions api/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,15 @@ export async function requestInput(question: string) {

return answer.trim()
}

export function stringify(data: any) {
if (typeof data === 'string') {
return data
}

if (typeof data === 'object' && Buffer.isBuffer(data)) {
return data.toString()
}

return JSON.stringify(data) + ''
}
21 changes: 20 additions & 1 deletion homebridge/camera-source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,18 @@ export class CameraSource {
request: { width: number; height: number },
callback: (err?: Error, snapshot?: Buffer) => void
) {
const start = Date.now()
try {
const start = Date.now()
this.logger.info(`Snapshot Requested for ${this.ringCamera.name}`)

if (this.ringCamera.isOffline) {
this.logger.error(
`Cannot retrieve snapshot because ${this.ringCamera.name} is offline. Make sure it has power and a good wifi connection.`
)
callback(new Error('Offline'))
return
}

const snapshot = await this.ringCamera.getSnapshot(true),
duration = (Date.now() - start) / 1000
this.logger.info(
Expand All @@ -87,6 +96,16 @@ export class CameraSource {
// HomeKit does a good job of resizing and doesn't seem to care if it's not right
callback(undefined, snapshot)
} catch (e) {
this.logger.error(
`Failed to retrieve snapshot for ${
this.ringCamera.name
} (${getDurationSeconds(
start
)}s). Make sure the camera has power and a good wifi connection. The camera currently reports that is it ${
this.ringCamera.isOffline ? 'offline' : 'online'
}`
)
this.logger.error(e)
callback(e)
}
}
Expand Down
4 changes: 2 additions & 2 deletions homebridge/camera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ export class Camera extends BaseAccessory<RingCamera> {
this.registerCharacteristic(
Characteristic.StatusLowBattery,
Service.BatteryService,
data => {
return data.alerts.battery === 'low'
() => {
return device.hasLowBattery
? StatusLowBattery.BATTERY_LEVEL_LOW
: StatusLowBattery.BATTERY_LEVEL_NORMAL
}
Expand Down

0 comments on commit adc08f6

Please sign in to comment.