Skip to content

Commit

Permalink
Initial Commit for Wake On Lan
Browse files Browse the repository at this point in the history
  • Loading branch information
tmrobert8 committed Jul 28, 2017
1 parent 41685ce commit dfbbeab
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 2 deletions.
159 changes: 159 additions & 0 deletions device/WakeOnLan/controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
'use strict';

const debug = require('debug');
const BluePromise = require('bluebird');
const fs = require('fs')
const readline = require('readline')
const os = require('os');
const net = require('net');
const dgram = require('dgram');

const PORT_NUMBER = 9;
const DEFAULT_IPADDRESS = '255.255.255.255';

let wolMap = "wol.map";

/**
* Sets the path to the 'wol.map' file
*/
module.exports.setMapPath = (mapPath) => {
wolMap = mapPath;
}

/**
* Handles button presses for WOL
*/
module.exports.onButtonPressed = (deviceid, name) => {
console.log(`WOL button pressed for ${name}`);

const addr = parseAddr(name);
const macAddr = getMacBuffer(addr.macAddress);

if (macAddr) {
let magic = Buffer.alloc(102, 0xff);
for (let i = 6; i < magic.length; i+=6) {
macAddr.copy(magic, i, 0);
}

debug("Sending WOL packet");
sendPacket(addr.ipAddress, magic)
.catch((err) => {
console.error("Error occurred writing WOL packet. ", err || err.message);
});

} else {
debug('MAC address was invalid and is ignored - %s', addr.macAddress);
}
}

/**
* Returns a promise to send the magic packet to the specified IP address
* @param {any} ipAddress the ipaddress to send to
* @param {any} magic the magic packet to send
*/
function sendPacket(ipAddress, magic) {
return new BluePromise((resolve, reject) => {
const socket = dgram.createSocket(net.isIPv6(ipAddress) ? 'udp6' : 'udp4')
.on('error', (err) => {
socket.close();
reject(err);
})
.once('listening', () => {
socket.setBroadcast(true);
});

socket.send(magic, 0, magic.length, PORT_NUMBER, ipAddress, (err, res) => {
socket.close();
if (err) {
reject(new Error("Exception writing the magic packet: " + err));
} else {
resolve(true);
}
});
});
}

/**
* Discovers devices from the wolMap file. Please note that validation of the
* address field is not
*/
module.exports.discoverWolDevices = () => {
debug('WOL discovery using %s', wolMap);

return new BluePromise((resolve, reject) => {
const devices = [];

readline.createInterface({ input: fs.createReadStream(wolMap) })
.on('line', (line) => {
const parsedLine = parseLine(line);
if (parsedLine) {
var parsedAddr = parseAddr(parsedLine.addr);
const macAddr = getMacBuffer(parsedAddr.macAddress);
if (macAddr) {
debug('WOL discovery call - found %s at %s', parsedLine.name, parsedLine.addr);
devices.push({
id: parsedLine.addr,
name: parsedLine.name,
reachable: true
});
} else {
debug("MAC address is invalid and will be ignored: %s", parsedAddr.macAddress);
}
}
})
.on('error', (err) => {
reject(err);
})
.on('close', () => {
resolve(devices);
});
});
}

/**
* Parses a line from the map file and returns undefined (if the line can't be parsed
* or an object containing a name and addr field.
* @param {any} line the line to parse
*/
function parseLine(line) {
if (!line.startsWith("#")) {
const idx = line.indexOf("=");
if (idx >= 0) {
return {
name : line.substring(0, idx).trim(),
addr : line.substring(idx + 1).trim().replace(/ /g, '/')
}
}
}
return undefined;
}

/**
* Parses the string address (found in the wol.map file) to a macAddress/ipAddress
* @param {any} addr the address field to parse
*/
function parseAddr(addr) {
const idx = addr.indexOf("/");
return {
macAddress: (idx < 0 ? addr : addr.substring(0, idx)).trim(),
ipAddress: (idx < 0 ? DEFAULT_IPADDRESS : addr.substring(idx + 1)).trim(),
};
}

/**
* Parses the String (hex) MAC address to a byte array of integers
* @param {any} macAddress the max address
*/
function getMacBuffer(macAddress) {
const hexArray = macAddress.split(/[-:]+/);
if (hexArray.length === 6) {
const buffer = Buffer.alloc(6);
for (let i = 0; i < 6; i++) {
buffer[i] = parseInt(hexArray[i], 16);
}
return buffer;
}

return undefined;
}

55 changes: 55 additions & 0 deletions device/WakeOnLan/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use strict';

const neeoapi = require('neeo-sdk');

console.log('NEEO SDK Example "WakeOnLan"');
console.log('------------------------------------------');

const controller = require('./controller');

/**
* Uncomment the following line to set the path to the wol map file
*/
//controller.setMapPath("device/WakeOnLan/wol.map");

const wolDevice = neeoapi.buildDevice('WakeOnLan')
.setManufacturer('NEEO')
.addAdditionalSearchToken('WOL')
.setType('ACCESSOIRE')
.addButton({ name: 'wol', label: 'Wake Up' })
.addButtonHander(controller.onButtonPressed)
.enableDiscovery({
headerText: 'WOL Instructions',
description: 'Please make sure you add devices to wol.map'
}, controller.discoverWolDevices);

function startSdkExample(brain) {
console.log('- Start server');
neeoapi.startServer({
brain,
port: 6336,
name: 'wake-on-lan',
devices: [wolDevice]
})
.then(() => {
console.log('# READY, use the mobile app to search for "WakeOnLan"');
})
.catch((error) => {
console.error('ERROR!', error.message);
process.exit(1);
});
}

const brainIp = process.env.BRAINIP;
//const brainIp = '192.168.1.29';
if (brainIp) {
console.log('- use NEEO Brain IP from env variable', brainIp);
startSdkExample(brainIp);
} else {
console.log('- discover one NEEO Brain...');
neeoapi.discoverOneBrain()
.then((brain) => {
console.log('- Brain discovered:', brain.name);
startSdkExample(brain);
});
}
20 changes: 20 additions & 0 deletions device/WakeOnLan/wol.map
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#####################################################################################
# This file will provide mappings between devices and their MAC address/IP addresses
#
# The format of the file is
# Device Name=MACAddress[/IPAddress]
#
# 1) (Required) The device name will appear on the NEEO remote
# 2) (Required) The MAC address to send the WOL packet to
# 3) (Optional) Slash or space followed by the device IP address
#
# If IP Address is specified, the WOL packet is sent directly to it. If not
# specified, the WOL packet will be broadcast to the network
#
# Please note that if the MACAddress appears twice (or MAC Address/IP Address combo),
# the NEEO app will complain of a duplicate when trying to add a device.
#####################################################################################

Example=01:02:03:04:05:06
IP Example=01:02:03:04:05:06 192.168.1.111
IP Example2=01:02:03:04:05:06/192.168.1.112
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
"private": false,
"dependencies": {
"bluebird": "^3.5.0",
"neeo-sdk": "*",
"lifx-http-api": "^1.0.3"
"lifx-http-api": "^1.0.3",
"debug": "*",
"neeo-sdk": "*"
},
"engines": {
"node": ">=6.0.0"
Expand Down

0 comments on commit dfbbeab

Please sign in to comment.