mirror of
https://github.com/brmlab/brmdoor.git
synced 2025-06-07 19:24:01 +02:00
Replace brmdoor.php by full-fledged universal hub script brmd
brmd should handle various kinds of brmlab-related events (brmdoor unlocks, status panel events, wiki updates) and provide central place to handle them, with unified interface (web and IRC). Currently, all of this should be implemented, except wiki updates handling. But the script is still a bit rudimentary; some TODO items are mentioned in the TODO file.
This commit is contained in:
parent
3d15892bf8
commit
4e017cb3db
5 changed files with 278 additions and 91 deletions
33
TODO
Normal file
33
TODO
Normal file
|
@ -0,0 +1,33 @@
|
|||
* Support for brmwiki update notifications.
|
||||
Watch the RSS feed.
|
||||
|
||||
* Potentially some prettier icons and richer javascript stubs.
|
||||
|
||||
|
||||
* Have the brmlab sketch do bi-directional TCP communication.
|
||||
Currently, it only sends stuff and cannot receive any
|
||||
instructions. This is pre-req for features below.
|
||||
Alternatively, we may decide we need to switch back to
|
||||
USB communication (with reliable USB host) if the ethernet
|
||||
shield proves to be too unreliable.
|
||||
|
||||
* Way to override status from web page.
|
||||
To keep status LED and web page in sync, bi-directional
|
||||
communication will be required.
|
||||
|
||||
* Way to unlock door remotely (through some secure channel).
|
||||
Bi-directional communication will be required.
|
||||
|
||||
|
||||
* Webcam support.
|
||||
We will need to figure out where to connect it.
|
||||
|
||||
* Picture snapshot support.
|
||||
Add a button to the panel that will make the webcam
|
||||
take a snapshot - useful for letting the world know
|
||||
who came in, or to publish random stuff. Post to twitpic
|
||||
or some-such.
|
||||
|
||||
* Live-feed support.
|
||||
Turn webcam on/off based on the second switch. Route the
|
||||
stream to some public place.
|
245
brmd.pl
Normal file
245
brmd.pl
Normal file
|
@ -0,0 +1,245 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use POE qw(Component::IRC Component::Client::TCP Component::Server::HTTP);
|
||||
use HTTP::Status qw/RC_OK/;
|
||||
|
||||
our $channel = "#brmlab";
|
||||
our ($status, $record, $topic) = (0, 0, 'BRMLAB OPEN');
|
||||
|
||||
my $irc = POE::Component::IRC->spawn(
|
||||
nick => 'brmbot',
|
||||
ircname => 'The Brmlab Automaton',
|
||||
server => 'irc.freenode.org',
|
||||
) or die "Oh noooo! $!";
|
||||
|
||||
my $door = POE::Component::Client::TCP->new(
|
||||
RemoteAddress => "192.168.1.3",
|
||||
RemotePort => 23,
|
||||
ServerInput => \&brmdoor_input,
|
||||
) or die "Oh naaaay! $!";
|
||||
|
||||
my $web = POE::Component::Server::HTTP->new(
|
||||
Port => 8088,
|
||||
ContentHandler => {
|
||||
"/brmstatus.html" => \&web_brmstatus_html,
|
||||
"/brmstatus.js" => \&web_brmstatus_js,
|
||||
"/brmstatus.png" => \&web_brmstatus_png,
|
||||
"/" => \&web_index
|
||||
},
|
||||
Headers => {Server => 'brmd/xxx'},
|
||||
) or die "Oh neee! $!";
|
||||
|
||||
|
||||
|
||||
POE::Session->create(
|
||||
package_states => [
|
||||
main => [ qw(_default _start irc_001 irc_public irc_332 irc_topic) ],
|
||||
],
|
||||
heap => { irc => $irc },
|
||||
);
|
||||
|
||||
$poe_kernel->run();
|
||||
|
||||
|
||||
sub _start {
|
||||
my $heap = $_[HEAP];
|
||||
|
||||
# retrieve our component's object from the heap where we stashed it
|
||||
my $irc = $heap->{irc};
|
||||
|
||||
$irc->yield( register => 'all' );
|
||||
$irc->yield( connect => { } );
|
||||
}
|
||||
|
||||
sub _default {
|
||||
my ($event, $args) = @_[ARG0 .. $#_];
|
||||
my @output = ( "$event: " );
|
||||
|
||||
for my $arg (@$args) {
|
||||
if ( ref $arg eq 'ARRAY' ) {
|
||||
push( @output, '[' . join(', ', @$arg ) . ']' );
|
||||
}
|
||||
else {
|
||||
push( @output, "'$arg'" );
|
||||
}
|
||||
}
|
||||
print join ' ', @output, "\n";
|
||||
}
|
||||
|
||||
sub status_str {
|
||||
$status ? 'OPEN' : 'CLOSED';
|
||||
}
|
||||
|
||||
sub record_str {
|
||||
$record ? 'ON AIR' : 'OFF AIR';
|
||||
}
|
||||
|
||||
|
||||
## Brmdoor
|
||||
|
||||
sub brmdoor_input {
|
||||
my $input = $_[ARG0];
|
||||
$input =~ /^(\d) (\d) (.*)$/ or return;
|
||||
my ($cur_status, $cur_record, $brm) = ($1, $2, $3);
|
||||
if ($cur_status != $status) {
|
||||
$status = $cur_status;
|
||||
my $st = status_str();
|
||||
$irc->yield (privmsg => $channel => "[brmstatus] update: \002$st" );
|
||||
my $newtopic = $topic;
|
||||
if ($status) {
|
||||
$newtopic =~ s/BRMLAB CLOSED/BRMLAB OPEN/g;
|
||||
} else {
|
||||
$newtopic =~ s/BRMLAB OPEN/BRMLAB CLOSED/g;
|
||||
}
|
||||
if ($newtopic ne $topic) {
|
||||
$topic = $newtopic;
|
||||
$irc->yield (topic => $channel => $topic );
|
||||
}
|
||||
}
|
||||
if ($cur_record != $record) {
|
||||
$record = $cur_record;
|
||||
my $st = record_str();
|
||||
$irc->yield (privmsg => $channel => "[brmvideo] update (TODO): \002$st" );
|
||||
}
|
||||
if ($brm =~ s/^CARD //) {
|
||||
print "from brmdoor: $input\n";
|
||||
if ($brm =~ /^UNKNOWN/) {
|
||||
$irc->yield (privmsg => $channel => "[brmdoor] unauthorized access denied!" );
|
||||
} else {
|
||||
$irc->yield (privmsg => $channel => "[brmdoor] unlocked by: \002$brm" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
## Web interface
|
||||
|
||||
sub disable_caching {
|
||||
my ($response) = @_;
|
||||
$response->push_header("Cache-Control", "no-cache, must-revalidate");
|
||||
$response->push_header("Expires", "Sat, 26 Jul 1997 05:00:00 GMT");
|
||||
}
|
||||
|
||||
sub web_index {
|
||||
my ($request, $response) = @_;
|
||||
|
||||
my $sts = status_str();
|
||||
my $str = record_str();
|
||||
|
||||
$response->code(RC_OK);
|
||||
$response->push_header("Content-Type", "text/html");
|
||||
disable_caching($response);
|
||||
|
||||
$response->content(<<EOT
|
||||
<html>
|
||||
<head><title>brmd</title></head>
|
||||
<body>
|
||||
<img src="http://brmlab.cz/lib/tpl/brmlab/images/brmlab_logo.png" alt="brmlab" align="right" />
|
||||
<h1>brmd web interface</h1>
|
||||
<p>Enjoy the view!</p>
|
||||
<ul>
|
||||
<li><strong>brmstatus</strong> ($sts) <a href="brmstatus.html">status page</a> | <a href="brmstatus.js">javascript code</a> | <a href="brmstatus.png">picture</a></li>
|
||||
<li><strong>brmvideo</strong> ($str) live feed coming soon!</li>
|
||||
</ul>
|
||||
</body></html>
|
||||
EOT
|
||||
);
|
||||
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
sub web_brmstatus_html {
|
||||
my ($request, $response) = @_;
|
||||
|
||||
my $bg = $status ? 'lightgreen' : '#DEE7EC';
|
||||
my $st = $status ? 'OPEN' : 'CLOSED';
|
||||
|
||||
$response->code(RC_OK);
|
||||
$response->push_header("Content-Type", "text/html");
|
||||
disable_caching($response);
|
||||
|
||||
$response->content(<<EOT
|
||||
<html>
|
||||
<head><title>brmstatus</title></head>
|
||||
<body bgcolor="$bg">
|
||||
<h1 align="center">brmlab is $st</h1>
|
||||
</body></html>
|
||||
EOT
|
||||
);
|
||||
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
sub web_brmstatus_js {
|
||||
my ($request, $response) = @_;
|
||||
|
||||
$response->code(RC_OK);
|
||||
$response->push_header("Content-Type", "text/javascript");
|
||||
disable_caching($response);
|
||||
|
||||
$response->content(<<EOT
|
||||
function brmstatus() { return ($status); }
|
||||
EOT
|
||||
);
|
||||
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
sub web_brmstatus_png {
|
||||
my ($request, $response) = @_;
|
||||
|
||||
open my $img, ($status ? "status-open.png" : "status-closed.png");
|
||||
local $/;
|
||||
my $imgdata = <$img>;
|
||||
close $img;
|
||||
|
||||
$response->code(RC_OK);
|
||||
$response->push_header("Content-Type", "image/png");
|
||||
disable_caching($response);
|
||||
|
||||
$response->content($imgdata);
|
||||
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
|
||||
## IRC
|
||||
|
||||
sub irc_001 {
|
||||
my $sender = $_[SENDER];
|
||||
|
||||
# Since this is an irc_* event, we can get the component's object by
|
||||
# accessing the heap of the sender. Then we register and connect to the
|
||||
# specified server.
|
||||
my $irc = $sender->get_heap();
|
||||
|
||||
print "Connected to ", $irc->server_name(), "\n";
|
||||
|
||||
$irc->yield( join => $channel );
|
||||
}
|
||||
|
||||
sub irc_public {
|
||||
my ($sender, $who, $where, $what) = @_[SENDER, ARG0 .. ARG2];
|
||||
my $nick = ( split /!/, $who )[0];
|
||||
my $channel = $where->[0];
|
||||
|
||||
if ( my ($rot13) = $what =~ /^rot13 (.+)/ ) {
|
||||
$rot13 =~ tr[a-zA-Z][n-za-mN-ZA-M];
|
||||
$irc->yield( privmsg => $channel => "$nick: $rot13" );
|
||||
}
|
||||
}
|
||||
|
||||
sub irc_332 {
|
||||
my ($sender, $server, $str, $data) = @_[SENDER, ARG0 .. ARG2];
|
||||
$topic = $data->[1];
|
||||
print "new topic: $topic\n"
|
||||
}
|
||||
|
||||
sub irc_topic {
|
||||
my ($sender, $who, $where, $what) = @_[SENDER, ARG0 .. ARG2];
|
||||
my $channel = $where;
|
||||
$topic = $what;
|
||||
print "new topic: $topic\n"
|
||||
}
|
91
brmdoor.php
91
brmdoor.php
|
@ -1,91 +0,0 @@
|
|||
<?php
|
||||
|
||||
function post_irc($open) {
|
||||
|
||||
$server = 'irc.freenode.net';
|
||||
$port = 6667;
|
||||
$nick = 'brmdoor';
|
||||
$ident = 'brmdoor';
|
||||
$host = 'brmlab.cz';
|
||||
$chan = '#brmlab';
|
||||
$name = 'Brmlab Gatekeeper';
|
||||
|
||||
$fp = fsockopen($server, $port, $errno, $errstr, 30);
|
||||
|
||||
if (!$fp) return;
|
||||
|
||||
fwrite($fp, "USER $ident $host $nick :$name\r\n");
|
||||
fwrite($fp, "NICK $nick\r\n");
|
||||
fwrite($fp, "JOIN $chan\r\n");
|
||||
|
||||
$topic = '';
|
||||
while (!feof($fp)) {
|
||||
$line = fgets($fp,256);
|
||||
$data = explode(' ', $line, 5);
|
||||
if ($data[1] == '366') break;
|
||||
if ($data[1] == '332') {
|
||||
$topic = $data[4];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$topic) {
|
||||
$topic = $open ? 'BRMLAB OPEN' : 'BRMLAB CLOSED';
|
||||
} else {
|
||||
$topic = explode('|', $topic, 2);
|
||||
@ $topic = ($open ? 'BRMLAB OPEN' : 'BRMLAB CLOSED') . ' | ' . trim($topic[1]);
|
||||
}
|
||||
|
||||
fwrite($fp, "TOPIC $chan :$topic\r\n");
|
||||
while (!feof($fp)) {
|
||||
$line = fgets($fp,256);
|
||||
$data = explode(' ', $line, 3);
|
||||
if ($data[1] == 'TOPIC') break;
|
||||
}
|
||||
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function get_state() {
|
||||
$h = fopen('brmdoor.txt', 'r');
|
||||
$contents = fread($h, 128);
|
||||
$r = $contents[0] == '1';
|
||||
fclose($h);
|
||||
return $r;
|
||||
}
|
||||
|
||||
function set_state($open) {
|
||||
$h = fopen('brmdoor.txt', 'w');
|
||||
fwrite($h, $open ? '1' : '0');
|
||||
fclose($h);
|
||||
}
|
||||
|
||||
if ($_POST['state']) {
|
||||
if ($_POST['state'] == 'OPEN') {
|
||||
set_state(true);
|
||||
post_irc(true);
|
||||
}
|
||||
if ($_POST['state'] == 'CLOSE') {
|
||||
set_state(false);
|
||||
post_irc(false);
|
||||
}
|
||||
}
|
||||
|
||||
$state = get_state();
|
||||
|
||||
?>
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Brmdoor</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1><i>brmlab is</i> <?php echo $state ? 'open' : 'closed'; ?></h1>
|
||||
<form action="" method="post">
|
||||
<input type="submit" name="state" value="OPEN"<?php if ($state) echo " disabled"; ?>>
|
||||
<input type="submit" name="state" value="CLOSE"<?php if (!$state) echo " disabled"; ?>>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
BIN
status-closed.png
Normal file
BIN
status-closed.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
BIN
status-open.png
Normal file
BIN
status-open.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
Loading…
Add table
Add a link
Reference in a new issue