Skip to content

Commit

Permalink
Merge pull request #1 from madpilot/cec
Browse files Browse the repository at this point in the history
Cec
  • Loading branch information
madpilot committed Dec 15, 2016
2 parents 2f6b50c + 5604b3b commit 732d2ff
Show file tree
Hide file tree
Showing 9 changed files with 300 additions and 18 deletions.
4 changes: 4 additions & 0 deletions software/.gitignore
@@ -0,0 +1,4 @@
/virtuals/*
!/virtuals/.keep
/custom
/node_modules
15 changes: 15 additions & 0 deletions software/config.json
Expand Up @@ -17,6 +17,21 @@
"config": {
"discover": true
}
},
{
"driver": "cec",
"config": {
"devices": [
{ "name": "tv", "address": 0 },
{
"name": "receiver",
"address": 5,
"overrides": {
"KEY_AV1": "custom/yamaha-hacks.js"
}
}
]
}
}
]
}
135 changes: 135 additions & 0 deletions software/devices/cec.js
@@ -0,0 +1,135 @@
var CEC = require('cec_node');

var CONSTANTS = CEC.CONSTANTS;
var USER_CONTROL_CODES = CONSTANTS.USER_CONTROL_CODES;

var KEYS = {
KEY_SELECT: USER_CONTROL_CODES.SELECT,
KEY_UP: USER_CONTROL_CODES.UP,
KEY_DOWN: USER_CONTROL_CODES.DOWN,
KEY_LEFT: USER_CONTROL_CODES.LEFT,
KEY_RIGHT: USER_CONTROL_CODES.RIGHT,
KEY_SETUP: USER_CONTROL_CODES.SETUP,
KEY_FAVOURITES: USER_CONTROL_CODES.FAVOURITE_MENU,
KEY_0: USER_CONTROL_CODES.KEY_0,
KEY_1: USER_CONTROL_CODES.KEY_1,
KEY_2: USER_CONTROL_CODES.KEY_2,
KEY_3: USER_CONTROL_CODES.KEY_3,
KEY_4: USER_CONTROL_CODES.KEY_4,
KEY_5: USER_CONTROL_CODES.KEY_5,
KEY_6: USER_CONTROL_CODES.KEY_6,
KEY_7: USER_CONTROL_CODES.KEY_7,
KEY_8: USER_CONTROL_CODES.KEY_8,
KEY_9: USER_CONTROL_CODES.KEY_9,
KEY_DOT: USER_CONTROL_CODES.DOT,
KEY_ENTER: USER_CONTROL_CODES.ENTER,
KEY_CLEAR: USER_CONTROL_CODES.CLEAR,
KEY_CHANNELUP: USER_CONTROL_CODES.CHANNEL_UP,
KEY_CHANNELDOWN: USER_CONTROL_CODES.CHANNEL_DOWN,
KEY_SOUND: USER_CONTROL_CODES.SOUND,
KEY_INFO: USER_CONTROL_CODES.DISPLAY_INFO,
KEY_HELP: USER_CONTROL_CODES.HELP,
KEY_PAGEUP: USER_CONTROL_CODES.PAGEUP,
KEY_PAGEDOWN: USER_CONTROL_CODES.PAGEDOWN,
KEY_POWER: USER_CONTROL_CODES.POWER,
KEY_VOLUMEUP: USER_CONTROL_CODES.VOLUME_UP,
KEY_VOLUMEDOWN: USER_CONTROL_CODES.VOLUME_DOWN,
KEY_MUTE: USER_CONTROL_CODES.MUTE,
KEY_PLAY: USER_CONTROL_CODES.PLAY,
KEY_STOP: USER_CONTROL_CODES.STOP,
KEY_PAUSE: USER_CONTROL_CODES.PAUSE,
KEY_RECORD: USER_CONTROL_CODES.RECORD,
KEY_REWIND: USER_CONTROL_CODES.REWIND,
KEY_FASTFORWARD: USER_CONTROL_CODES.FAST_FORWARD,
KEY_EJECTCD: USER_CONTROL_CODES.EJECT,
KEY_FORWARD: USER_CONTROL_CODES.FORWARD,
KEY_BACKWARD: USER_CONTROL_CODES.BACKWARD,
KEY_ANGLE: USER_CONTROL_CODES.ANGLE,
KEY_EPG: USER_CONTROL_CODES.EPG,
KEY_BLUE: USER_CONTROL_CODES.BLUE,
KEY_RED: USER_CONTROL_CODES.RED,
KEY_GREEN: USER_CONTROL_CODES.GREEN,
KEY_YELLOW: USER_CONTROL_CODES.YELLOW,
KEY_STANDBY: USER_CONTROL_CODES.POWER_OFF_FUNCTION
}

function CECDevice(cec, device, address, overrides) {
this.cec = cec;
this.source = 1;
this.destination = address;
this.overrides = {};

for(var key in overrides) {
this.overrides[key] = require("../" + overrides[key]);
}
}

CECDevice.initialize = function(config, cb) {
var devices = {}
var cec = CEC.start({ name: 'ir-blaster' });

for(var i = 0; i < config.devices.length; i++) {
var device = config.devices[i];
var receiver = new CECDevice(cec, device.name, device.address, device.overrides);
devices['cec-' + device.name] = receiver;
}

cb(null, devices);
}

CECDevice.prototype.buildCode = function(opcode, data) {
return this.source + "" + this.destination + ":" + opcode + (data ? ":" + data : "");
}

CECDevice.prototype.list = function(callback) {
var keys = [];
for(var key in KEYS) {
keys.push(key);
}
for(var key in this.overrides) {
keys.push(key);
}

callback(null, keys);
}

CECDevice.prototype.sendOnce = function(key, cb) {
var context = this;
this.sendStart(key, function(error, data) {
if(error) {
cb(error);
return;
}
setTimeout(function() {
context.sendStop(key, cb);
}, 100);
});
}

function formatHex(num) {
return ("00" + num.toString(16)).substr(-2);
}

CECDevice.prototype.sendStart = function(key, cb) {
if(this.overrides[key]) {
this.overrides[key].call(this, key, cb);
} else {
var code = this.buildCode(44, formatHex(KEYS[key]));
this.cec.send(code, cb)
}
}

CECDevice.prototype.sendStop = function(key, cb) {
var code = this.buildCode(45);
this.cec.send(code, cb)
}

CECDevice.prototype.status = function(status, cb) {
cb("Not supported");
}

CECDevice.prototype.statuses = function(cb) {
cb(null, []);
}

module.exports = CECDevice;
55 changes: 55 additions & 0 deletions software/devices/virtual.js
@@ -0,0 +1,55 @@
var fs = require('fs');

function VirtualDevice(input) {
function Inner(config) {
this.config = config;
this.input = input;
}

Inner.initialize = function(config, cb) {
fs.readFile(config, function(error, data) {
if(error) {
cb(error);
return;
}

var device = {};
var virtual = JSON.parse(data);
device[virtual.name] = new Inner(config);
return device;
});
}

Inner.prototype.list = function(cb) {
var keys = [];
for(var key in this.config.macros) {
keys.push(key);
}
cb(null, keys);
}

Inner.prototype.sendOnce = function(key, cb) {
this.input.invoke(this.config.macros[key], cb);
}

Inner.prototype.sendStart = function(key, cb) {
// TODO, track which are started, and manually repeat
this.input.invoke(this.config.macros[key], cb);
}

Inner.prototype.sendStop = function(key) {
this.input.invoke(this.config.macros[key], cb);
}

Inner.prototype.status = function(status, cb) {
cb("Not supported");
}

Inner.prototype.statuses = function(cb) {
cb(null, []);
}

return Inner;
}

module.exports = VirtualDevice;
47 changes: 33 additions & 14 deletions software/index.js
Expand Up @@ -3,31 +3,33 @@ var logger = require('winston');
var config = require('./config.json');

var deviceDrivers = {
lirc: require('./devices/lirc.js')
lirc: require('./devices/lirc.js'),
cec: require('./devices/cec.js'),
virtual: require('./devices/virtual.js')
};

var inputDrivers = {
http: require('./inputs/http.js')
http: require('./inputs/http.js'),
virtual: require('./inputs/virtual.js')
}


var inputs = [];
for(var i = 0; i < config.inputs.length; i++) {
var input = config.inputs[i];
if(input.driver == "http") {
logger.info("Loading the http input driver");
var server = new inputDrivers.http(input.config);
inputs.push(server);
}
}
var virtualInput = new inputDrivers.virtual();

var devices = {};
for(var i = 0; i < config.devices.length; i++) {
var device = config.devices[i];
var driver = null;

if(device.driver == "lirc") {
driver = deviceDrivers.lirc;
switch(device.driver) {
case "lirc":
driver = deviceDrivers.lirc;
break;
case "cec":
driver = deviceDrivers.cec;
break;
case "virtual":
driver = deviceDrivers.virtual(virtualInput);
break;
}

if(driver != null) {
Expand All @@ -40,6 +42,23 @@ for(var i = 0; i < config.devices.length; i++) {
}
}

var inputs = [ virtualInput ];
for(var i = 0; i < config.inputs.length; i++) {
var input = config.inputs[i];
var server = null;

switch(input.driver) {
case "http":
logger.info("Loading the http input driver");
server = new inputDrivers.http(input.config);
break;
}

if(server != null) {
inputs.push(server);
}
}

function handler(device, event, key, cb) {
if(device == null) {
logger.info("Listing devices");
Expand Down
8 changes: 4 additions & 4 deletions software/inputs/http.js
Expand Up @@ -59,8 +59,8 @@ HttpInput.prototype.listen = function(handler) {
});
});

app.put("/start/:device/:key", function(req, res) {
handler(req.params.device, "sendStart", req.params.key, function(error, success) {
app.put("/start/:device", function(req, res) {
handler(req.params.device, "sendStart", req.body.key, function(error, success) {
setHeaders(res);
if(error != null) {
returnError(req, res, error);
Expand All @@ -70,8 +70,8 @@ HttpInput.prototype.listen = function(handler) {
});
});

app.put("/stop/:device/:key", function(req, res) {
handler(req.params.device, "sendStop", req.params.key, function(error, success) {
app.put("/stop/:device", function(req, res) {
handler(req.params.device, "sendStop", req.body.key, function(error, success) {
setHeaders(res);
if(error != null) {
returnError(req, res, error);
Expand Down
53 changes: 53 additions & 0 deletions software/inputs/virtual.js
@@ -0,0 +1,53 @@
function VirtualInput() {
this.hander = null;
};

VirtualInput.prototype.listen = function(handler) {
this.handler = handler;
}

VirtualInput.prototype.invoke = function(macros, cb) {
if(this.handler == null) {
cb();
}

var index = -1;
var results = [];

function waterfall(error, result) {
index++;

if(index == macros.length) {
cb(null, results);
return;
}

if(error) {
cb(error);
return;
}

if(results != null) {
results.push(result);
}

var macro = macros[index].split('/');
var event = macro[0];
var device = macro[1];
var key = macro[2];

if(event == "delay") {
setTimeout(function() {
waterfall();
}, device);
} else {
this.handler(device, event, key, waterfall);
}
}

waterfall();
}

VirtualInput.prototype.list = function(cb) {}

module.exports = VirtualInput;
1 change: 1 addition & 0 deletions software/package.json
Expand Up @@ -18,6 +18,7 @@
"homepage": "https://github.com/madpilot/ir-blaster#readme",
"dependencies": {
"body-parser": "^1.15.2",
"cec_node": "git+https://git@github.com/madpilot/cec_node.git",
"express": "^4.14.0",
"lirc_node": "0.0.4",
"winston": "^2.3.0"
Expand Down
Empty file added software/virtuals/.keep
Empty file.

0 comments on commit 732d2ff

Please sign in to comment.