var ssdp = require('node-upnp-ssdp'); var UPnPDeviceClient = require('upnp-device-client'); var mqtt = require('mqtt'); var clientId = 'kenta-upnp-gateway'; var mqttOptions = { clientId: clientId }; var mqttEndpoint = 'mqtt://beam.soracom.io'; var mqttClient = mqtt.connect(mqttEndpoint, mqttOptions); var topicUpdateAccepted = '$aws/things/' + clientId + '/shadow/update/accepted'; var topicUpdateDelta = '$aws/things/' + clientId + '/shadow/update/delta'; var topicDevicesRoot = '$aws/things/' + clientId + '/devices'; var devices = {}; var addOrUpdateDevice = function(info){ var udn = info.usn.split('::')[0]; if (!devices[udn]){ var client = new UPnPDeviceClient(info.location); client.getDeviceDescription(function(err, device){ if (!err){ console.log('UPnP: Device ' + device.friendlyName + ' found'); device.client = client; device.online = true; publish(createDevicePresense(udn, device)); devices[udn] = device; } else { console.error('UPnP: ' + err); } }); } else { publish(createDevicePresense(udn, {online: true})); } }; var createDevicePresense = function(udn, device){ var presense = { devices: {} }; presense.devices[udn] = {}; for (var key in device){ if (key !== 'client'){ presense.devices[udn][key] = device[key]; } } return presense; }; var removeDevice = function(info){ var udn = info.usn.split('::')[0]; var device = devices[udn]; if (device){ console.log('UPnP: Device ' + device.friendlyName + ' gets offline'); delete devices[udn]; publish(createDevicePresense(udn, {online: false})); } }; var publish = function(state){ var event = { state: { reported: state } }; mqttClient.publish('$aws/things/kenta-upnp-gateway/shadow/update', JSON.stringify(event)); }; var getContextFromTopic = function(topic){ var path = topic.substring(topicDevicesRoot.length + 1); var elems = path.split('/'); var context = { udn: elems[0], service: elems[1], action: elems[2] }; console.log(context); return context; }; var executeAction = function(context, args){ if (devices[context.udn]){ var client = devices[context.udn].client; client.callAction(context.service, context.action, args, function(err, result) { if (!err){ console.log(result); } else { console.error(err); } }); } else { console.error("No such device found"); } }; ssdp.on('DeviceFound', addOrUpdateDevice); ssdp.on('DeviceAvailable:upnp:rootdevice', addOrUpdateDevice); ssdp.on('DeviceUnavailable:upnp:rootdevice', removeDevice); ssdp.on('DeviceError', console.error); ssdp.mSearch('upnp:rootdevice'); mqttClient.on('connect', function () { mqttClient.subscribe([topicUpdateDelta, topicUpdateAccepted, topicDevicesRoot + '/#']); }); mqttClient.on('message', function (topic, buffer) { if (topic === topicUpdateAccepted){ console.log('MQTT: Device shadow update accepted by AWS IoT'); } else if (topic === topicUpdateDelta){ console.log('MQTT: Delta is received from AWS IoT'); var delta = JSON.parse(buffer.toString()); console.log(delta); } else if (topic.indexOf(topicDevicesRoot) === 0){ console.log('MQTT: Message to a device received'); executeAction(getContextFromTopic(topic), JSON.parse(buffer.toString())); } else { console.error('MQTT: Unknown message received on topic ' + topic); console.error(buffer.toString()); } }); mqttClient.on('error', console.error);