Skip to content

Instantly share code, notes, and snippets.

@moleike
Last active August 26, 2016 02:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save moleike/48fc4facc329ec428de44cdbaa3eb894 to your computer and use it in GitHub Desktop.
Save moleike/48fc4facc329ec428de44cdbaa3eb894 to your computer and use it in GitHub Desktop.
an http api for mqtt-shepherd
// reverse proxy (HTTP(s) to LWM2M over MQTT)
// this should be mounted on an Express app
const router = require('express').Router()
const shepherd = require('lib/shepherd');
const ch = require('lib/status'); // coap to http status
const herrero = require('herrero');
const bodyParser = require('body-parser')
const { NotFoundError } = herrero;
// load the router on e.g /device/:id
module.exports = router
// read returns object representations
// discover returns object links
// TODO add support for these media types:
//
// * application/senml+json for object representations
// * application/link-format+json for object links
//
router.use(bodyParser.json({ type: 'application/*+json' }))
router.param('oid', (req, res, next, id) => {
const qnode = shepherd.find(req.id); // assumes the device id is stored on req object
if (!qnode)
next(new NotFoundError(`not found`))
req.qnode = qnode;
next();
});
router.route('/:oid')
.get((req, res, next) => {
req.qnode.readReq(req.path, (err, rsp) => {
if (err) return next(err);
const { status, data } = rsp;
res.status(ch(status)).json(data);
});
})
router.route('/:oid/attributes')
.get((req, res, next) => {
console.log('req.param.oid ' + req.param.oid)
req.qnode.discoverReq(req.params.oid, (err, rsp) => {
if (err) return next(err);
const { status, data } = rsp;
res.status(ch(status)).json(data);
});
})
.put((req, res, next) => {
const { pmin, pmax, gt, lt, stp } = req.query;
req.qnode.writeAttrsReq(req.params.oid, {
pmin, pmax, gt, lt, stp
}, (err, rsp) => {
if (err) return next(err);
const { status, data } = rsp;
res.status(ch(status)).json(data);
});
});
router.route('/:oid/:iid')
.get((req, res, next) => {
req.qnode.readReq(req.path, (err, rsp) => {
if (err) return next(err);
const { status, data } = rsp;
res.status(ch(status)).json(data);
});
})
.put((req, res, next) => {
const { oid, iid } = req.params;
const { id : rid , value } = req.body.resource; // { resource: { id: 0, value: 'foo' } }
if (rid === undefined || value === undefined)
return next(new BadRequestError());
req.qnode.writeReq(`${oid}/${iid}/${rid}`, value, (err, rsp) => {
if (err) return next(err);
const { status, data } = rsp;
res.status(ch(status)).json(data);
});
})
router.route('/:oid/:iid/attributes')
.get((req, res, next) => {
const { oid, iid } = req.params;
req.qnode.discoverReq(`${oid}/${iid}`, (err, rsp) => {
if (err) return next(err);
const { status, data } = rsp;
res.status(ch(status)).json(data);
});
})
.put((req, res, next) => {
const { oid, iid } = req.params;
const { pmin, pmax, gt, lt, stp } = req.query;
req.qnode.writeAttrsReq(`${oid}/${iid}`, {
pmin, pmax, gt, lt, stp
}, (err, rsp) => {
if (err) return next(err);
const { status, data } = rsp;
res.status(ch(status)).json(data);
});
});
router.route('/:oid/:iid/:rid')
.get((req, res, next) => {
req.qnode.readReq(req.path, (err, rsp) => {
if (err) return next(err);
const { status, data } = rsp;
res.set('Content-Type', 'text/plain');
res.status(ch(status)).json(data);
});
})
router.route('/:oid/:iid/:rid/exec')
.post((req, res, next) => {
if (!Array.isArray(req.body.args)) // { 'args': [0, 'myString', false] }
return next(new BadRequestError('array of arguments was expected'));
req.qnode.executeReq(req.path, req.body ,(err, rsp) => {
if (err) return next(err);
const { status, data } = rsp;
res.status(ch(status)).json(data);
});
});
router.route('/:oid/:iid/:rid/attributes')
.get((req, res, next) => {
const { oid, iid, rid } = req.params;
req.qnode.discoverReq(`${oid}/${iid}/${rid}`, (err, rsp) => {
if (err) return next(err);
const { status, data } = rsp;
res.status(ch(status)).json(data);
});
})
.put((req, res, next) => {
const { oid, iid, rid } = req.params;
const { pmin, pmax, gt, lt, stp } = req.query;
req.qnode.writeAttrsReq(`${oid}/${iid}/${rid}`, {
pmin, pmax, gt, lt, stp
}, (err, rsp) => {
if (err) return next(err);
const { status, data } = rsp;
res.status(ch(status)).json(data);
});
})
// as in https://developer.github.com/v3/gists/#star-a-gist
router.route('/:oid/:iid/:rid/observe')
.put((req, res, next) => {
const { oid, iid, rid } = req.params;
req.qnode.observeReq(`${oid}/${iid}/${rid}`, (err, rsp) => {
if (err) return next(err);
const { status, data } = rsp;
res.status(ch(status)).json(data);
});
})
.delete((req, res, next) => {
const { oid, iid, rid } = req.params;
req.qnode.writeAttrsReq(`${oid}/${iid}/${rid}`, {
cancel: true
}, (err, rsp) => {
if (err) return next(err);
const { status, data } = rsp;
res.status(ch(status)).json(data);
});
});
const MqttShepherd = require('mqtt-shepherd');
const debug = require('debug')('dm:lwm2m');
const qserver = new MqttShepherd('device_manager', {
port: 1883
});
qserver.on('ready', function () {
debug('MQTT server ready.');
qserver.permitJoin(180);
});
qserver.on('message', function (topic, message, packet) {
debug(topic, message);
});
module.exports = qserver;
// TODO https://tools.ietf.org/html/draft-ietf-core-http-mapping-13#section-7
module.exports = code => coap_http[code];
const coap_http = {
201: 201, // Created
202: 200, // OK
203: 304, // Not Modified
204: 200, // OK
205: 200, // OK
400: 400, // Bad Request
401: 403, // Forbidden
402: 400, // Bad Request
403: 403, // Forbidden
404: 404, // Not Found
405: 400, // Bad Request
406: 406, // Not Acceptable
412: 412, // Precondition Failed
413: 413, // Request Repr. Too Large
415: 415, // Unsupported Media Type
500: 500, // Internal Server Error
501: 501, // Not Implemented
502: 502, // Bad Gateway
503: 503, // Service Unavailable
504: 504, // Gateway Timeout
505: 502 // Bad Gateway
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment