-
Notifications
You must be signed in to change notification settings - Fork 59
Reimplemetation of macro engine #39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: osur
Are you sure you want to change the base?
Changes from all commits
45a41f6
99df326
483e8db
6908d25
e06f40d
7a26ab9
2a48e3b
057b89b
9d3668b
e399ed4
a86b767
2f1d1c0
64381f8
58b8d6a
d3599be
9bc4aab
e5aa785
4501147
0d740e0
8b07b12
99920b6
f2327e4
daa0244
65fe787
6bd355b
ed1f7d9
29e5529
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,3 +7,5 @@ node_modules/ | |
| npm-debug.log | ||
| server.cert | ||
| server.key | ||
| .idea | ||
| *.iml | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -46,7 +46,6 @@ These are the available configuration options: | |
|
|
||
| #### Example config.json: | ||
|
|
||
|
|
||
| { | ||
| "server" : { | ||
| "port" : 3000, | ||
|
|
@@ -61,31 +60,38 @@ These are the available configuration options: | |
| "VolumeDown": true | ||
| } | ||
| }, | ||
| "macros": { | ||
| "Play Xbox 360": [ | ||
| [ "gpio", "TV", 1], | ||
| [ "gpio", "Receiver", 1], | ||
| [ "gpio", "Xbox", 1], | ||
| [ "SonyTV", "Power" ], | ||
| [ "delay", 500 ], | ||
| [ "SonyTV", "Xbox360" ], | ||
| [ "Yamaha", "Power" ], | ||
| [ "delay", 250 ], | ||
| [ "Yamaha", "Xbox360" ], | ||
| [ "Xbox360", "Power" ] | ||
| ], | ||
| "Listen to Music": [ | ||
| [ "gpio", "Receiver", 1], | ||
| [ "Yamaha", "Power" ], | ||
| [ "delay", 500 ], | ||
| [ "Yamaha", "AirPlay" ] | ||
| ], | ||
| "all off": [ | ||
| [ "gpio", "TV", 0], | ||
| [ "gpio", "Receiver", 0], | ||
| [ "gpio", "Xbox", 0] | ||
| ], | ||
| }, | ||
| "macros": [ | ||
| { | ||
| "name": "Xbox360", | ||
| "sequence": [ | ||
| [ "gpio", "TV", 1 ], | ||
| [ "gpio", "Receiver", 1 ], | ||
| [ "gpio", "Xbox", 1 ], | ||
| [ "SonyTV", "Power" ], | ||
| [ "SonyTV", "Xbox360" ], | ||
| [ "Yamaha", "Power" ], | ||
| [ "Yamaha", "Xbox360" ], | ||
| [ "Xbox360", "Power" ] | ||
| ]}, | ||
| { | ||
| "name": "lights off", | ||
| "defaultDelay": 40, | ||
| "sequence": [ | ||
| [ "Lightcontrol", "C01off"], | ||
| [ "Lightcontrol", "C02off"], | ||
| [ "Lightcontrol", "C03off"] | ||
| ]}, | ||
| { | ||
| "name": "all off", | ||
| "defaultDelay": 20, | ||
| "sequence": [ | ||
| [ "call", "lights off"], | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe that if this was called
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. since the sequence is just a property of the macro, but not the macro itself, it would lead to less intuitive expressions on the code side. Although I see your point, I'd suggest to keep it. |
||
| [ "gpio", "TV", 0 ], | ||
| [ "gpio", "Receiver", 0 ], | ||
| [ "gpio", "Xbox", 0 ] | ||
| ] | ||
| } | ||
| ], | ||
| "commandLabels": { | ||
| "Yamaha": { | ||
| "Power": "Power", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,14 +4,14 @@ | |
| var express = require('express'); | ||
| var logger = require('morgan'); | ||
| var compress = require('compression'); | ||
| var lircNode = require('lirc_node'); | ||
| var lirc = require('./lib/lirc'); | ||
| var consolidate = require('consolidate'); | ||
| var swig = require('swig'); | ||
| var labels = require('./lib/labels'); | ||
| var https = require('https'); | ||
| var fs = require('fs'); | ||
| var macros = require('./lib/macro-manager.js'); | ||
| var gpio = require('./lib/gpio'); | ||
| var macros = require('./lib/macros'); | ||
|
|
||
| // Precompile templates | ||
| var JST = { | ||
|
|
@@ -21,7 +21,7 @@ var JST = { | |
| // Create app | ||
| var app = module.exports = express(); | ||
|
|
||
| // lirc_web configuration | ||
| // osur configuration | ||
| var config = {}; | ||
| var hasServerPortConfig = false; | ||
| var hasSSLConfig = false; | ||
|
|
@@ -43,7 +43,7 @@ app.set('view engine', 'jade'); | |
| app.use(compress()); | ||
| app.use(express.static(__dirname + '/static')); | ||
|
|
||
| function _init() { | ||
| function readConfiguration() { | ||
| var searchPaths = []; | ||
|
|
||
| function configure(configFileName) { | ||
|
|
@@ -52,14 +52,13 @@ function _init() { | |
| console.log('Open Source Universal Remote is configured by ' + configFileName); | ||
| } | ||
|
|
||
| lircNode.init(); | ||
|
|
||
| // Config file is optional | ||
| try { | ||
| try { | ||
| configure(__dirname + '/config.json'); | ||
| } catch (e) { | ||
| configure(process.env.HOME + '/.lirc_web_config.json'); | ||
| console.log('DEBUG:', e); | ||
| configure(process.env.HOME + '/.osur-config.json'); | ||
| } | ||
| } catch (e) { | ||
| console.log('DEBUG:', e); | ||
|
|
@@ -73,89 +72,95 @@ function _init() { | |
| && config.server.ssl_key && config.server.ssl_port; | ||
| } | ||
|
|
||
| function refineRemotes(myRemotes) { | ||
| var newRemotes = {}; | ||
| var newRemoteCommands = null; | ||
| var remote = null; | ||
|
|
||
| function isBlacklistExisting(remoteName) { | ||
| return config.blacklists && config.blacklists[remoteName]; | ||
| function overrideConfigurationForDebugOrDevelopment() { | ||
| var lircTest; | ||
| if (process.env.npm_package_config_test_env) { | ||
| lircTest = require('./test/lib/lirc'); | ||
| lircTest.replaceLircByMock(); | ||
| gpio.setGpioLibrary(require('./lib/gpio-la-mock')); | ||
| config = require('./test/fixtures/config.json'); | ||
| hasServerPortConfig = false; | ||
| hasSSLConfig = false; | ||
| } else { | ||
| gpio.setGpioLibrary(require('./lib/gpio-la-gpio')); | ||
| } | ||
| } | ||
|
|
||
| function getCommandsForRemote(remoteName) { | ||
| var remoteCommands = myRemotes[remoteName]; | ||
| var blacklist = null; | ||
|
|
||
| if (isBlacklistExisting(remoteName)) { | ||
| blacklist = config.blacklists[remoteName]; | ||
| function initializeModules(done) { | ||
| function initializeMacros() { | ||
| var currentStates = macros.getCurrentStates(); | ||
| macros.resetConfiguration(); | ||
|
|
||
| remoteCommands = remoteCommands.filter(function (command) { | ||
| return blacklist.indexOf(command) < 0; | ||
| }); | ||
| if (config.gpios) { | ||
| macros.registerDevice(gpio); | ||
| } | ||
|
|
||
| return remoteCommands; | ||
| macros.registerDevice(lirc); | ||
| macros.init(config.macros, currentStates); | ||
| } | ||
|
|
||
| for (remote in myRemotes) { | ||
| newRemoteCommands = getCommandsForRemote(remote); | ||
| newRemotes[remote] = newRemoteCommands; | ||
| } | ||
| lirc.init(config, function () { | ||
| if (config.gpios) { | ||
| gpio.init(config.gpios); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason why
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah. Lirc consumes the whole configuration, including macros. This implies a lack of separation of concern, which leads to cross dependencies while initalizing both. I'd prefer a initialization like iterate on configured modules, initialize them, initialize macros. This would imply a restructuring of the configuration. Could we take it for now? I don't want to do two refactorings in one pull request. Or would you mind to suggest another aproach for perfoming initialisation? |
||
| } | ||
|
|
||
| return newRemotes; | ||
| } | ||
| if (config.macros) { | ||
| initializeMacros(); | ||
| } | ||
|
|
||
| // Based on node environment, initialize connection to lircNode or use test data | ||
| if (process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'development') { | ||
| lircNode.remotes = require(__dirname + '/test/fixtures/remotes.json'); | ||
| config = require(__dirname + '/test/fixtures/config.json'); | ||
| gpio.overrideWiringPi(require('./test/lib/wiring-pi-mock')); | ||
| } else { | ||
| _init(); | ||
| // initialize Labels for remotes / commands | ||
| labelFor = labels(config.remoteLabels, config.commandLabels); | ||
| done(); | ||
| }); | ||
| } | ||
|
|
||
| // initialize Labels for remotes / commands | ||
| labelFor = labels(config.remoteLabels, config.commandLabels); | ||
| function init(done) { | ||
| readConfiguration(); | ||
| overrideConfigurationForDebugOrDevelopment(); | ||
| initializeModules(done); | ||
| } | ||
|
|
||
| // Routes | ||
|
|
||
| // Index | ||
| app.get('/', function (req, res) { | ||
| var refinedRemotes = refineRemotes(lircNode.remotes); | ||
| res.send(JST.index({ | ||
| remotes: refinedRemotes, | ||
| macros: config.macros, | ||
| var indexPage = JST.index({ | ||
| remotes: lirc.getRemotes(), | ||
| macros: macros.getGuiMacroLabels(), | ||
| repeaters: config.repeaters, | ||
| gpios: config.gpios, | ||
| labelForRemote: labelFor.remote, | ||
| labelForCommand: labelFor.command, | ||
| })); | ||
| }); | ||
| res.send(indexPage); | ||
| }); | ||
|
|
||
| // Refresh | ||
| app.get('/refresh', function (req, res) { | ||
| _init(); | ||
| res.redirect('/'); | ||
| init(function () { | ||
| res.redirect('/'); | ||
| }); | ||
| }); | ||
|
|
||
| // List all remotes in JSON format | ||
| app.get('/remotes.json', function (req, res) { | ||
| res.json(refineRemotes(lircNode.remotes)); | ||
| res.json(lirc.getRemotes()); | ||
| }); | ||
|
|
||
| // List all commands for :remote in JSON format | ||
| app.get('/remotes/:remote.json', function (req, res) { | ||
| if (lircNode.remotes[req.params.remote]) { | ||
| res.json(refineRemotes(lircNode.remotes)[req.params.remote]); | ||
| var commands = lirc.getCommandsForRemote(req.params.remote); | ||
| if (commands) { | ||
| res.json(commands); | ||
| } else { | ||
| res.sendStatus(404); | ||
| } | ||
| }); | ||
|
|
||
| function respondWithGpioState(res) { | ||
| if (config.gpios) { | ||
| gpio.updatePinStates(); | ||
| res.json(config.gpios); | ||
| gpio.updatePinStates(config.gpios, function (result) { | ||
| res.json(result); | ||
| }); | ||
| } else { | ||
| res.send(404); | ||
| } | ||
|
|
@@ -171,49 +176,42 @@ app.get('/macros.json', function (req, res) { | |
| res.json(config.macros); | ||
| }); | ||
|
|
||
| // List all commands for :macro in JSON format | ||
| app.get('/macros/:macro.json', function (req, res) { | ||
| if (config.macros && config.macros[req.params.macro]) { | ||
| res.json(config.macros[req.params.macro]); | ||
| } else { | ||
| res.sendStatus(404); | ||
| } | ||
| }); | ||
|
|
||
| // Send :remote/:command one time | ||
| app.post('/remotes/:remote/:command', function (req, res) { | ||
| lircNode.irsend.send_once(req.params.remote, req.params.command, function () {}); | ||
| lirc.sendOnce(req.params.remote, req.params.command, function () {}); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a lot cleaner. |
||
| res.setHeader('Cache-Control', 'no-cache'); | ||
| res.sendStatus(200); | ||
| }); | ||
|
|
||
| // Start sending :remote/:command repeatedly | ||
| app.post('/remotes/:remote/:command/send_start', function (req, res) { | ||
| lircNode.irsend.send_start(req.params.remote, req.params.command, function () {}); | ||
| lirc.sendStart(req.params.remote, req.params.command, function () {}); | ||
| res.setHeader('Cache-Control', 'no-cache'); | ||
| res.sendStatus(200); | ||
| }); | ||
|
|
||
| // Stop sending :remote/:command repeatedly | ||
| app.post('/remotes/:remote/:command/send_stop', function (req, res) { | ||
| lircNode.irsend.send_stop(req.params.remote, req.params.command, function () {}); | ||
| lirc.sendStop(req.params.remote, req.params.command, function () {}); | ||
| res.setHeader('Cache-Control', 'no-cache'); | ||
| res.sendStatus(200); | ||
| }); | ||
|
|
||
| // toggle /gpios/:gpio_pin | ||
| app.post('/gpios/:gpio_pin', function (req, res) { | ||
| var newValue = gpio.togglePin(req.params.gpio_pin); | ||
| res.setHeader('Cache-Control', 'no-cache'); | ||
| res.json(newValue); | ||
| res.end(); | ||
| gpio.togglePin(req.params.gpio_pin, function (result) { | ||
| res.setHeader('Cache-Control', 'no-cache'); | ||
| res.json(result); | ||
| res.end(); | ||
| }); | ||
| }); | ||
|
|
||
|
|
||
| // Execute a macro (a collection of commands to one or more remotes) | ||
| app.post('/macros/:macro', function (req, res) { | ||
| if (config.macros && config.macros[req.params.macro]) { | ||
| macros.exec(config.macros[req.params.macro], lircNode); | ||
| var macroName = req.params.macro; | ||
| if (macros.isMacroDefined(macroName)) { | ||
| macros.execute(macroName); | ||
| res.setHeader('Cache-Control', 'no-cache'); | ||
| if (config.gpios) { | ||
| respondWithGpioState(res); | ||
|
|
@@ -226,27 +224,27 @@ app.post('/macros/:macro', function (req, res) { | |
| } | ||
| }); | ||
|
|
||
| gpio.init(config.gpios); | ||
|
|
||
| // Listen (http) | ||
| if (hasServerPortConfig) { | ||
| port = config.server.port; | ||
| } | ||
| // only start server, when called as application | ||
| if (!module.parent) { | ||
| app.listen(port); | ||
| console.log('Open Source Universal Remote UI + API has started on port ' + port + ' (http).'); | ||
| } | ||
| init(function () { | ||
| // only start server, when called as application | ||
| if (!module.parent) { | ||
| // Listen (http) | ||
| if (hasServerPortConfig) { | ||
| port = config.server.port; | ||
| } | ||
|
|
||
| // Listen (https) | ||
| if (hasSSLConfig) { | ||
| sslOptions = { | ||
| key: fs.readFileSync(config.server.ssl_key), | ||
| cert: fs.readFileSync(config.server.ssl_cert), | ||
| }; | ||
| app.listen(port); | ||
| console.log('Open Source Universal Remote UI + API has started on port ' + port + ' (http).'); | ||
|
|
||
| https.createServer(sslOptions, app).listen(config.server.ssl_port); | ||
| // Listen (https) | ||
| if (hasSSLConfig) { | ||
| sslOptions = { | ||
| key: fs.readFileSync(config.server.ssl_key), | ||
| cert: fs.readFileSync(config.server.ssl_cert), | ||
| }; | ||
| https.createServer(sslOptions, app).listen(config.server.ssl_port); | ||
|
|
||
| console.log('Open Source Universal Remote UI + API has started on port ' | ||
| + config.server.ssl_port + ' (https).'); | ||
| } | ||
| console.log('Open Source Universal Remote UI + API has started on port ' | ||
| + config.server.ssl_port + ' (https).'); | ||
| } | ||
| } | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if it makes sense to add
iras the first parameter here to specify which communication protocol to use. This might make further enhancements (zwave,http) easier.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
currently
lircis a optional first argument to serve this purpose. Possible are any permutations of:irI'd choose both, if you agree.