homespun
Version:
This is the root of the homespun family of repositories: you run a server in your home that collects sensor readings and uploads them to the management cloud of your choice. At present, upload to only one cloud is available: [numerous](http://numerousapp.
154 lines (121 loc) • 4.73 kB
JavaScript
/*
* based on sonos 0.6.1's lib/events/listener.js
*/
var cadence = require('cadence/redux')
, underscore = require('underscore')
, UserAgent = require('vizsla')
require('cadence/loops')
// jscs:disable requireMultipleVarDecl
var Listener = function (invoker, device) {
this.event = invoker.event.bind(invoker)
this.device = device
this.services = {}
this.stopP = false
this.ua = new UserAgent()
}
// jscs:enable requireMultipleVarDecl
Listener.prototype.initialize = cadence(function (async, location) {
this.location = location
this.stopP = false
var loop = async(function () {
var now = new Date().getTime()
async.forEach(function (sid) {
var service = this.services[sid]
if (now < service.renew) return
async(function () {
this.ua.fetch(
{ url : 'http://' + this.device.host + ':' + this.device.port + service.endpoint
, method : 'SUBSCRIBE'
, headers :
{ SID : sid
, Timeout : 'Second-3600'
}
}, async())
}, function (body, response) {
if (response.statusCode === 200) {
this.services[sid].renew = this._renew_at(response.headers.timeout)
return
}
delete this.services[sid]
this.event('close', this.device
, { statusCode : response.statusCode
, body : body
}, async())
})
})(underscore.keys(this.services))
}, function () {
if (this.stopP) return [ loop ]
setTimeout(async(), 1 * 1000)
})()
})
Listener.prototype.finalize = cadence(function (async) {
this.stopP = true
async.forEach(function (sid) {
this.removeService(sid, async())
})(underscore.keys(this.services))
})
Listener.prototype.downcall = cadence(function (async, request) {
var items, service
if (!this.services[request.headers.sid]) return
service = this.services[request.headers.sid]
async(function () {
items = {}
underscore.each(request.body['e:propertyset']['e:property'], function (element) {
underscore.each(underscore.keys(element), function (key) { items[key] = element[key][0] })
})
this.event('update', this.device
, { endpoint : service.endpoint
, sid : request.headers.sid
, items : items
}, async())
})
})
Listener.prototype.addService = cadence(function (async, serviceEndpoint) {
if (!this.location) throw 'Service is not initialized'
async(function () {
this.ua.fetch(
{ url : 'http://' + this.device.host + ':' + this.device.port + serviceEndpoint
, method : 'SUBSCRIBE'
, headers :
{ callback : '<' + this.location + '>'
, NT : 'upnp:event'
, Timeout : 'Second-3600'
}
}, async())
}, function (body, response) {
if (response.statusCode !== 200) return { statusCode : response.statusCode
, body : body
}
this.services[response.headers.sid] =
{ renew : this._renew_at(response.headers.timeout)
, endpoint : serviceEndpoint
}
return response.headers.sid
})
})
Listener.prototype.removeService = cadence(function (async, sid) {
if (!this.services[sid]) throw 'Service with sid ' + sid + ' is not registered'
async(function () {
this.ua.fetch(
{ url : 'http://' + this.device.host + ':' + this.device.port
+ this.services[sid].endpoint
, method : 'UNSUBSCRIBE'
, headers :
{ sid : sid
}
}, async())
}, function (body, response) {
if (response.statusCode !== 200) return { statusCode : response.statusCode
, body : body
}
})
})
Listener.prototype._renew_at = function (timeout) {
var seconds
if ((!!timeout) && (timeout.indexOf('Second-') === 0)) timeout = timeout.substr(7)
seconds = (((!!timeout) && (!isNaN(timeout))) ? parseInt(timeout, 10) : 3600) - 15
if (seconds < 0) seconds = 15
else if (seconds > 300) seconds = 300
return (new Date().getTime() + (seconds * 1000))
};
module.exports = Listener