Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ var https = require('https');
var fs = require('fs');
var gpio = require('./lib/gpio');
var macros = require('./lib/macros');
var request = require('request');

// Precompile templates
var JST = {
Expand Down Expand Up @@ -122,8 +123,10 @@ labelFor = labels(config.remoteLabels, config.commandLabels);
// Index
app.get('/', function (req, res) {
var refinedRemotes = refineRemotes(lircNode.remotes);

res.send(JST.index({
remotes: refinedRemotes,
devices: config.devices,
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

httpDevices or webDevices may make more sense here.

macros: config.macros,
repeaters: config.repeaters,
gpios: config.gpios,
Expand All @@ -138,6 +141,14 @@ app.get('/refresh', function (req, res) {
res.redirect('/');
});

// Get all capabilities of remote
app.get('/capabilities.json', function (req, res) {
res.json({
devices: config.devices,
remotes: lircNode.remotes,
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@OvisMaximus do you think gpio pins should be listed here, too?

});
});

// List all remotes in JSON format
app.get('/remotes.json', function (req, res) {
res.json(refineRemotes(lircNode.remotes));
Expand Down Expand Up @@ -180,6 +191,70 @@ app.get('/macros/:macro.json', function (req, res) {
}
});

// List all devices in JSON format
app.get('/devices.json', function (req, res) {
// TODO: Should return a nicely formatted response, not just the config object
res.json(config.devices);
});

// List all commands for :device in JSON format
app.get('/devices/:device.json', function (req, res) {
if (config.devices && config.devices[req.params.device]) {
// TODO: Should return a nicely formatted response, not just the config object
res.json(config.devices[req.params.device]);
} else {
res.send(404);
}
});

// Get device state
app.get('/devices/:device', function (req, res) {
var state = null;
var bodyJson = null;
var stateReq = null;

if (config.devices &&
config.devices[req.params.device] &&
config.devices[req.params.device].state) {
state = config.devices[req.params.device].state;
bodyJson = null;

// Build request to make to get device state
stateReq = {
method: state.method || 'GET',
url: state.url,
form: state.body,
};

// Make request for state, include body of response in response
request(stateReq, function (error, response, body) {
// TODO: How to parse the response and only return relevant portion?
bodyJson = JSON.parse(body);
res.json(bodyJson);
});
} else {
// Device doesn't have a state, return 404
res.sendStatus(404);
}
});

// Execute device action
app.post('/devices/:device/:command', function (req, res) {
var device = config.devices[req.params.device];
var command = device.commands[req.params.command];

var commandReq = {
method: command.method,
url: command.url,
form: command.body,
};

request(commandReq);

res.setHeader('Cache-Control', 'no-cache');
res.sendStatus(200);
});

// Send :remote/:command one time
app.post('/remotes/:remote/:command', function (req, res) {
lircNode.irsend.send_once(req.params.remote, req.params.command, function () {});
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"mocha": "2.3.4",
"nodemon": "1.8.1",
"should": "^8.1.1",
"request": "~2.53.0",
"sinon": "1.17.2",
"supertest": "1.1.0"
},
Expand Down
23 changes: 16 additions & 7 deletions static/css/app.less
Original file line number Diff line number Diff line change
Expand Up @@ -91,26 +91,33 @@ h1 {
background: @colorGreyBtn !important;
}

.device-link.active {
background: #5dade2 !important;
}

.btn-wide {
padding-left: 0;
padding-right: 0;
width: 100%;
}

.remote {
.remote,
.device {
margin: 0;
padding: 0;
position: relative;
}

.remotes-nav,
.macros-nav,
.gpios-nav {
.devices-nav,
.gpios-nav,
.macros-nav {
}

.remotes-nav li,
.macros-nav li,
.gpios-nav li {
.devices-nav li,
.gpios-nav li,
.macros-nav li {
margin: 20px 0;
}

Expand Down Expand Up @@ -138,11 +145,13 @@ a:active,
margin: 0 40px;
}

.remote {
.remote,
.device {
display: none;
}

.remote.active {
.remote.active,
.device.active {
display: block;
}

Expand Down
23 changes: 15 additions & 8 deletions static/css/compiled/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ ul {
padding: 0;
}
h1 {
background: #34495e;
background: #34495E;
color: #fff;
font-family: LatoBold;
margin: 0 0 30px;
Expand All @@ -34,13 +34,13 @@ h1 {
width: 100%;
}
h1.is-remote {
background: #3498db;
background: #3498DB;
}
.command {
margin: 20px 0;
}
.command:nth-child(even) .btn-primary {
background: #16a085;
background: #16A085;
}
.no-touch .command-link:hover {
background: #2fe2bf;
Expand All @@ -63,19 +63,24 @@ h1.is-remote {
.gpio-link.active {
background: #cccccc !important;
}
.device-link.active {
background: #5dade2 !important;
}
.btn-wide {
padding-left: 0;
padding-right: 0;
width: 100%;
}
.remote {
.remote,
.device {
margin: 0;
padding: 0;
position: relative;
}
.remotes-nav li,
.macros-nav li,
.gpios-nav li {
.devices-nav li,
.gpios-nav li,
.macros-nav li {
margin: 20px 0;
}
.back {
Expand All @@ -99,10 +104,12 @@ a:active,
#container {
margin: 0 40px;
}
.remote {
.remote,
.device {
display: none;
}
.remote.active {
.remote.active,
.device.active {
display: block;
}
.hidden {
Expand Down
16 changes: 15 additions & 1 deletion static/js/app/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,22 +133,36 @@ $(function() {
$('.back').on('click', function(evt) {
$('.remote.active').removeClass('active');
$('.remotes-nav').removeClass('hidden');
$('.devices-nav').removeClass('hidden');
$('.macros-nav').removeClass('hidden');
$('.gpios-nav').removeClass('hidden');
$('.back').addClass('hidden');
$('hr').removeClass('hidden');
$('#title').html($('#title').attr('data-text'));
$('#titlebar').removeClass('is-remote');
});

// Navigate to remote pages
$('.remotes-nav a').on('click', function(evt) {
$('.remotes-nav a, .devices-nav a').on('click', function(evt) {
evt.preventDefault();
var href = $(this).attr('href');
$('.remotes-nav').addClass('hidden');
$('.devices-nav').addClass('hidden');
$('.macros-nav').addClass('hidden');
$('.gpios-nav').addClass('hidden');
$(href).addClass('active');
$('.back').removeClass('hidden');
$('hr').addClass('hidden');
$('#title').html($('#title').attr('data-text'));
$('#titlebar').removeClass('is-remote');
});

// Navigate to remote pages
$('.remotes-nav a, .devices-nav a').on('click', function(evt) {
evt.preventDefault();
var href = $(this).attr('href');
$('.remotes-nav').addClass('hidden');
$('.devices-nav').addClass('hidden');
$('#title').html($(this).html());
$('#titlebar').addClass('is-remote');
});
Expand Down
2 changes: 1 addition & 1 deletion static/js/compiled/app.js

Large diffs are not rendered by default.

31 changes: 30 additions & 1 deletion templates/index.swig
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@
{% endfor %}
</ul>

<hr />

<ul class="devices-nav">
{% for device in devices %}
{% set deviceName = device.name %}
<li><a class="btn btn-wide btn-large btn-inverse" href="#{{ deviceName|url_encode }}">{{ deviceName }}</a></li>
{% endfor %}
</ul>


<hr />

<ul class="macros-nav">
Expand All @@ -53,13 +63,32 @@
<ul class="commands">
{% for command in remote %}
<li class="command">
<button class="command-link {% if repeaters[remoteName] && repeaters[remoteName][command] %}command-repeater{% else %}command-once{% endif %} btn btn-wide btn-large btn-primary" href="/remotes/{{ remoteName|url_encode }}/{{ command|url_encode }}">{{ labelForCommand(remoteName, command) }}</button>
<button class="remote-link command-link {% if repeaters[remoteName] && repeaters[remoteName][command] %}command-repeater{% else %}command-once{% endif %} btn btn-wide btn-large btn-primary" href="/remotes/{{ remoteName|url_encode }}/{{ command|url_encode }}">{{ labelForCommand(remoteName, command) }}</button>
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>

<hr />

<ul class="remotes">
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be devices

{% for device in devices %}
{% set deviceName = device.name %}
<li class="remote" id="{{ deviceName|url_encode }}">
<ul class="commands">
{% for command in device.commands %}
{% set commandName = loop.key %}
<li class="command">
<button class="device-link command-link {% if repeaters[remoteName] && repeaters[remoteName][command] %}command-repeater{% else %}command-once{% endif %} btn btn-wide btn-large btn-primary" href="/devices/{{ deviceName }}/{{ commandName|url_encode }}">{{ commandName }}</button>
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>

</div>

<div class="next"></div>
Expand Down
23 changes: 22 additions & 1 deletion test/fixtures/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,26 @@
"pin": 16},
{"name": "Xbox",
"pin": 13}
]
],
"devices": {
"GarageDoor": {
"name": "GarageDoor",
"commands": {
"TriggerDoor": {
"url": "https://api.spark.io/v1/devices/123/do_thing",
"method": "POST",
"body": {
"access_token": "abcdefg"
}
}
},
"state": {
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't used, so could be removed.

"url": "https://api.spark.io/v1/devices/123/get_state",
"method": "POST",
"body": {
"access_token": "abcdefg"
}
}
}
}
}
16 changes: 14 additions & 2 deletions test/lirc_web.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,18 +109,30 @@ describe('lirc_web', function () {
assert(response.headers['content-type'].match(/html/));
});

it('should return an HTML document in which all button elements of class command-link have an '
it('should return an HTML document in which all button elements of class remote-link have an '
+ 'href of the form /remotes/:remote/:command', function () {
assert.equal(error, null);

$('button.command-link').each(function (idx, elem) {
$('button.remote-link').each(function (idx, elem) {
var s = $(elem).attr('href').split('/');
assert.equal(4, s.length);
assert.equal('', s[0]);
assert.equal('remotes', s[1]);
});
});

it('should return an HTML document in which all button elements of class device-link have an '
+ 'href of the form /devices/:device/:command', function () {
assert.equal(error, null);

$('button.device-link').each(function (idx, elem) {
var s = $(elem).attr('href').split('/');
assert.equal(4, s.length);
assert.equal('', s[0]);
assert.equal('devices', s[1]);
});
});

it('should apply remotes configured labels', function () {
$('ul.remotes-nav').each(function (idx, elem) {
assert(elem.textContent.match(/LIRC namespace/) !== null);
Expand Down