2015-05-12 16:59:57 +01:00
|
|
|
_ = require 'lodash'
|
|
|
|
Promise = require 'bluebird'
|
|
|
|
knex = require './db'
|
|
|
|
utils = require './utils'
|
|
|
|
{ resinApi } = require './request'
|
2015-07-20 21:02:37 +01:00
|
|
|
device = exports
|
2015-05-12 16:59:57 +01:00
|
|
|
|
2015-07-20 21:02:37 +01:00
|
|
|
exports.getID = do ->
|
2015-05-12 16:59:57 +01:00
|
|
|
deviceIdPromise = null
|
|
|
|
return ->
|
|
|
|
# We initialise the rejected promise just before we catch in order to avoid a useless first unhandled error warning.
|
|
|
|
deviceIdPromise ?= Promise.rejected()
|
|
|
|
# Only fetch the device id once (when successful, otherwise retry for each request)
|
|
|
|
deviceIdPromise = deviceIdPromise.catch ->
|
|
|
|
Promise.all([
|
|
|
|
knex('config').select('value').where(key: 'apiKey')
|
|
|
|
knex('config').select('value').where(key: 'uuid')
|
|
|
|
])
|
|
|
|
.spread ([{value: apiKey}], [{value: uuid}]) ->
|
|
|
|
resinApi.get(
|
|
|
|
resource: 'device'
|
|
|
|
options:
|
|
|
|
select: 'id'
|
|
|
|
filter:
|
|
|
|
uuid: uuid
|
|
|
|
customOptions:
|
|
|
|
apikey: apiKey
|
|
|
|
)
|
|
|
|
.then (devices) ->
|
|
|
|
if devices.length is 0
|
|
|
|
throw new Error('Could not find this device?!')
|
|
|
|
return devices[0].id
|
|
|
|
|
|
|
|
|
|
|
|
# Calling this function updates the local device state, which is then used to synchronise
|
|
|
|
# the remote device state, repeating any failed updates until successfully synchronised.
|
|
|
|
# This function will also optimise updates by merging multiple updates and only sending the latest state.
|
2015-07-20 21:02:37 +01:00
|
|
|
exports.updateState = do ->
|
2015-05-12 16:59:57 +01:00
|
|
|
applyPromise = Promise.resolve()
|
|
|
|
targetState = {}
|
|
|
|
actualState = {}
|
|
|
|
|
|
|
|
getStateDiff = ->
|
|
|
|
_.omit targetState, (value, key) ->
|
|
|
|
actualState[key] is value
|
|
|
|
|
|
|
|
applyState = ->
|
2015-09-02 19:19:24 +00:00
|
|
|
stateDiff = getStateDiff()
|
|
|
|
if _.size(stateDiff) is 0
|
2015-05-12 16:59:57 +01:00
|
|
|
return
|
|
|
|
applyPromise = Promise.join(
|
|
|
|
knex('config').select('value').where(key: 'apiKey')
|
2015-07-20 21:02:37 +01:00
|
|
|
device.getID()
|
2015-05-12 16:59:57 +01:00
|
|
|
([{value: apiKey}], deviceID) ->
|
|
|
|
stateDiff = getStateDiff()
|
2015-09-02 19:19:24 +00:00
|
|
|
if _.size(stateDiff) is 0 || !apiKey?
|
2015-05-12 16:59:57 +01:00
|
|
|
return
|
|
|
|
resinApi.patch
|
|
|
|
resource: 'device'
|
|
|
|
id: deviceID
|
|
|
|
body: stateDiff
|
|
|
|
customOptions:
|
|
|
|
apikey: apiKey
|
|
|
|
.then ->
|
|
|
|
# Update the actual state.
|
|
|
|
_.merge(actualState, stateDiff)
|
|
|
|
)
|
2015-09-02 19:19:24 +00:00
|
|
|
.catch (error) ->
|
|
|
|
utils.mixpanelTrack('Device info update failure', {error, stateDiff})
|
|
|
|
# Delay 5s before retrying a failed update
|
|
|
|
Promise.delay(5000)
|
2015-05-12 16:59:57 +01:00
|
|
|
.finally ->
|
|
|
|
# Check if any more state diffs have appeared whilst we've been processing this update.
|
|
|
|
applyState()
|
|
|
|
|
|
|
|
return (updatedState = {}, retry = false) ->
|
|
|
|
# Remove any updates that match the last we successfully sent.
|
|
|
|
_.merge(targetState, updatedState)
|
|
|
|
|
|
|
|
# Only trigger applying state if an apply isn't already in progress.
|
|
|
|
if !applyPromise.isPending()
|
|
|
|
applyState()
|
|
|
|
return
|