2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-08 13:24:56 +00:00

32_yowsup.pm: added module

git-svn-id: https://svn.fhem.de/fhem/trunk@8755 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
justme-1968 2015-06-15 18:30:37 +00:00
parent 10ff218320
commit b6744c9f22
3 changed files with 548 additions and 0 deletions

View File

@ -1,5 +1,6 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it.
- added: 32_yowsup.pm: New module to send and receive WhatsApp messages
- feature: new module FB_CALLLIST for creating a call list based on
events generated by FB_CALLMONITOR (original idea by Elektolurch)
- feature: 01_FHEMWEB: add cmdIcon

546
fhem/FHEM/32_yowsup.pm Normal file
View File

@ -0,0 +1,546 @@
# $Id$
package main;
use strict;
use warnings;
use Socket;
use IO::Handle;
sub
yowsup_Initialize($)
{
my ($hash) = @_;
$hash->{ReadFn} = "yowsup_Read";
$hash->{DefFn} = "yowsup_Define";
$hash->{NOTIFYDEV} = "global";
$hash->{NotifyFn} = "yowsup_Notify";
$hash->{UndefFn} = "yowsup_Undefine";
$hash->{SetFn} = "yowsup_Set";
#$hash->{GetFn} = "yowsup_Get";
$hash->{AttrFn} = "yowsup_Attr";
$hash->{AttrList} = "disable:1 ";
$hash->{AttrList} .= "acceptFrom cmd ". $readingFnAttributes;
}
#####################################
sub
yowsup_Define($$)
{
my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def);
return "Usage: define <name> yowsup" if(@a < 2);
my $name = $a[0];
my $number = $a[2];
if( !defined($number) ) {
my $d = $modules{yowsup}{defptr}{yowsup};
return "yowsup MASTER already defined as $d->{NAME}." if( defined($d) && $d->{NAME} ne $name );
$modules{yowsup}{defptr}{yowsup} = $hash;
} else {
return "no yowsup MASTER defined." if( !defined($modules{yowsup}{defptr}{yowsup}) );
my $d = $modules{yowsup}{defptr}{$number};
return "yowsup $number already defined as $d->{NAME}." if( defined($d) && $d->{NAME} ne $name );
$modules{yowsup}{defptr}{$number} = $hash;
addToDevAttrList( $name, "commandPrefix" );
addToDevAttrList( $name, "allowedCommands" );
$hash->{NUMBER} = $number;
}
$hash->{NAME} = $name;
if( $init_done ) {
yowsup_Disconnect($hash);
yowsup_Connect($hash);
} elsif( $hash->{STATE} ne "???" ) {
$hash->{STATE} = "Initialized";
}
return undef;
}
sub
yowsup_Notify($$)
{
my ($hash,$dev) = @_;
return if($dev->{NAME} ne "global");
return if(!grep(m/^INITIALIZED|REREADCFG$/, @{$dev->{CHANGED}}));
yowsup_Connect($hash);
}
sub
yowsup_reConnect($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
Log3 $name, 3, "$name: reConnect";
yowsup_Disconnect($hash);
yowsup_Connect($hash);
}
sub
yowsup_Connect($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
return undef if( $hash->{NUMBER} );
return undef if( AttrVal($name, "disable", 0 ) == 1 );
$hash->{PARTIAL} = "";
my ($yowsup_child, $parent);
if( socketpair($yowsup_child, $parent, AF_UNIX, SOCK_STREAM, PF_UNSPEC) ) {
$yowsup_child->autoflush(1);
$parent->autoflush(1);
my $pid = fhemFork();
if(!defined($pid)) {
close $parent;
close $yowsup_child;
my $msg = "$name: Cannot fork: $!";
Log 1, $msg;
return $msg;
}
if( $pid ) {
close $parent;
$hash->{STATE} = "Connected";
$hash->{CONNECTS}++;
$hash->{FH} = $yowsup_child;
$hash->{FD} = fileno($yowsup_child);
$hash->{PID} = $pid;
$hash->{WAITING_FOR_LOGIN} = 1;
$selectlist{$name} = $hash;
} else {
close $yowsup_child;
close STDIN;
close STDOUT;
my $fn = $parent->fileno();
open(STDIN, "<&$fn") or die "can't redirect STDIN $!";
open(STDOUT, ">&$fn") or die "can't redirect STDOUT $!";
#select STDIN; $| = 1;
#select STDOUT; $| = 1;
#STDIN->autoflush(1);
STDOUT->autoflush(1);
close $parent;
$ENV{PYTHONUNBUFFERED} = 1;
my $cmd = AttrVal($name, "cmd", "/opt/local/bin/yowsup-cli demos -c /root/config.yowsup --yowsup" );
Log3 $name, 2, "$name: starting yoswup-cli: $cmd";
exec split( ' ', $cmd ) or Log 1, "exec failed!!!";
POSIX::_exit(0);;
}
} else {
#$hash->{STATE} = "Connected";
Log3 $name, 3, "$name: socketpair failed";
InternalTimer(gettimeofday()+20, "yowsup_Connect", $hash, 0);
}
}
sub
yowsup_Disconnect($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
return undef if( $hash->{NUMBER} );
RemoveInternalTimer($hash);
return if( !$hash->{FD} );
if( $hash->{PID} ) {
yowsup_Write($hash, '/disconnect' );
kill( 9, $hash->{PID} );
waitpid($hash->{PID}, 0);
delete $hash->{PID};
}
close($hash->{CD}) if($hash->{CD});
delete($hash->{FD});
delete($hash->{CD});
delete($selectlist{$name});
$hash->{STATE} = "Disconnected";
Log3 $name, 3, "$name: Disconnected";
$hash->{LAST_DISCONNECT} = FmtDateTime( gettimeofday() );
}
sub
yowsup_Undefine($$)
{
my ($hash, $arg) = @_;
yowsup_Disconnect($hash);
if( $hash->{NUMBER} ) {
delete $modules{yowsup}{defptr}{$hash->{NUMBER}};
} else {
delete $modules{yowsup}{defptr}{yowsup};
}
return undef;
}
sub
yowsup_Set($$@)
{
my ($hash, $name, $cmd, @args) = @_;
my $list = "";
if( $hash->{NUMBER} ) {
my $phash = $modules{yowsup}{defptr}{yowsup};
$list .= "image send" if( $phash->{PID} );
if( $cmd eq 'image' ) {
return "MASTER not connected" if( !$phash->{PID} );
return yowsup_Write( $phash, "/image send $hash->{NUMBER} $args[0]" );
return undef;
} elsif( $cmd eq 'send' ) {
return "MASTER not connected" if( !$phash->{PID} );
return yowsup_Write( $phash, "/message send $hash->{NUMBER} '". join( ' ', @args ) ."'" );
return undef;
}
} else {
$list .= "image send raw disconnect:noArg " if( $hash->{PID} );
$list .= "reconnect:noArg";
if( $cmd eq 'raw' ) {
return yowsup_Write( $hash, join( ' ', @args ) );
return undef;
} elsif( $cmd eq 'image' ) {
return yowsup_Write( $hash, "/image send $args[0] $args[1]" );
return undef;
} elsif( $cmd eq 'send' ) {
return yowsup_Write( $hash, "/message send ". shift(@args) ." '". join( ' ', @args ) ."'" );
return undef;
} elsif( $cmd eq 'disconnect' ) {
yowsup_Disconnect($hash);
return undef;
} elsif( $cmd eq 'reconnect' ) {
yowsup_Disconnect($hash);
yowsup_Connect($hash);
return undef;
}
}
return "Unknown argument $cmd, choose one of $list";
}
sub
yowsup_Get($$@)
{
my ($hash, $name, $cmd) = @_;
my $list = "devices:noArg";
if( $cmd eq "devices" ) {
return undef;
}
return "Unknown argument $cmd, choose one of $list";
}
sub
yowsup_Parse($$)
{
my ($hash,$data) = @_;
my $name = $hash->{NAME};
Log3 $name, 4, "$name: parse: $data";
$hash->{TIME} = TimeNow();
RemoveInternalTimer($hash);
InternalTimer(gettimeofday()+60*10, "yowsup_reConnect", $hash, 0);
if( $data =~ m/\[offline\]:/ ) {
readingsSingleUpdate( $hash, "state", 'offline', 1 ) if( ReadingsVal($name,'state','' ) ne 'offline' );
if( $hash->{WAITING_FOR_LOGIN} ) {
yowsup_Write( $hash, '/L' );
yowsup_Write( $hash, '/presence available' );
#yowsup_Write( $hash, '/ping' );
delete $hash->{WAITING_FOR_LOGIN};
}
} elsif( $data =~ m/\[connected\]:/ ) {
readingsSingleUpdate( $hash, "state", 'connected', 1 ) if( ReadingsVal($name,'state','' ) ne 'connected' );
} elsif( $data =~ m/Auth: Logged in!/ ) {
readingsSingleUpdate( $hash, "state", 'logged in', 1 ) if( ReadingsVal($name,'state','' ) ne 'logged in' );
}
if( $data =~ m/^CHATSTATE:.*State: (\S*).*From: (\d*)/s ) {
my $chatstate = $1;
my $number = $2;
if( my $chash = $modules{yowsup}{defptr}{$number} ) {
readingsSingleUpdate( $chash, "chatstate", $chatstate, 1 );
}
#} elsif( $data =~ m/\[(.*)@.*\((.*)\)\]:\[(.*)\]\s*(.*)/s ) {
} elsif( $data =~ m/\[(.*)@.*\((.*)\)\]:\[([^\]]*)\]\s*(.*)(\nMessage)/s
|| $data =~ m/\[(.*)@.*\((.*)\)\]:\[([^\]]*)\]\s*(.*)/s ) {
my $number = $1;
my $time = $2;
my $id = $3;
my $message = $4;
my $chash = $modules{yowsup}{defptr}{$number};
if( !$chash ) {
my $accept_from = AttrVal($name, "acceptFrom", undef );
if( !$accept_from || ",$accept_from," =~/,$number,/ ) {
my $define = "$number yowsup $number";
my $cmdret = CommandDefine(undef,$define);
if($cmdret) {
Log3 $name, 1, "$name: Autocreate: An error occurred while creating device for number '$number': $cmdret";
} else {
#$cmdret = CommandAttr(undef,"$number alias ".$result->{$id}{name});
$cmdret = CommandAttr(undef,"$number room yowsup");
#$cmdret = CommandAttr(undef,"$number IODev $name");
}
$chash = $modules{yowsup}{defptr}{$number};
}
}
if( $chash ) {
readingsSingleUpdate( $chash, "message", $message, 1 );
readingsSingleUpdate( $chash, "chatstate", "received", 1 );
my $cname = $chash->{NAME};
if( my $prefix = AttrVal($cname, "commandPrefix", undef ) ) {
my $cmd;
if( $prefix eq '0' ) {
} elsif( $prefix eq '1' ) {
$cmd = $message;
} elsif( $message =~ m/^$prefix(.*)/ ) {
$cmd = $1;
}
if( $cmd ) {
Log3 $name, 3, "$cname: received command: $cmd";
my $allowed = AttrVal($cname, "allowedCommands", undef );
my $ret = AnalyzeCommandChain( $hash, $cmd, $allowed );
Log3 $name, 4, "$cname: command result: $ret";
yowsup_Write( $hash, "/message send $chash->{NUMBER} '$ret'" ) if( $ret );
}
}
} else {
Log3 $name, 3, "$name: sender: $number not allowed";
}
}
}
sub
yowsup_Read($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
my $buf;
my $ret = sysread($hash->{FH}, $buf, 65536 );
if(!defined($ret) || $ret <= 0) {
yowsup_Disconnect( $hash );
Log3 $name, 3, "$name: read: error during sysread: $!" if(!defined($ret));
Log3 $name, 3, "$name: read: end of file reached while sysread" if( $ret <= 0);
InternalTimer(gettimeofday()+10, "yowsup_Connect", $hash, 0);
return undef;
}
yowsup_Parse($hash,$buf);
return undef;
my $data = $hash->{PARTIAL};
Log3 $name, 5, "yowsup/RAW: $data/$buf";
$data .= $buf;
$hash->{PARTIAL} = $data;
}
sub
yowsup_Write($$)
{
my ($hash, $data) = @_;
my $name = $hash->{NAME};
return "not connected" if( !$hash->{PID} );
$data =~ s/\n/ /g;
Log3 $name, 3, "$name: sending $data";
syswrite $hash->{FH}, $data ."\n";
return undef;
}
sub
yowsup_Attr($$$)
{
my ($cmd, $name, $attrName, $attrVal) = @_;
my $orig = $attrVal;
if( $attrName eq "disable" ) {
my $hash = $defs{$name};
yowsup_Disconnect($hash);
if( $cmd eq "set" && $attrVal ne "0" ) {
$attrVal = 1;
} else {
$attr{$name}{$attrName} = 0;
yowsup_Connect($hash);
}
} elsif( $attrName eq "cmd" ) {
my $hash = $defs{$name};
if( $cmd eq "set" ) {
$attr{$name}{$attrName} = $attrVal;
} else {
delete $attr{$name}{$attrName};
}
yowsup_Disconnect($hash);
yowsup_Connect($hash);
}
if( $cmd eq "set" ) {
if( !defined($orig) || $orig ne $attrVal ) {
$attr{$name}{$attrName} = $attrVal;
return $attrName ." set to ". $attrVal;
}
}
return;
}
1;
=pod
=begin html
<a name="yowsup"></a>
<h3>yowsup</h3>
<ul>
Module to interface to the yowsup library to send and recive WhatsApp messages.<br><br>
Notes:
<ul>
<li>Probably only works on linux/unix systems.</li>
</ul><br>
<a name="yowsup_Define"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; yowsup</code><br>
<br>
Defines a yowsup device.<br><br>
Examples:
<ul>
<code>define WhatsApp yowsup</code><br>
</ul>
</ul><br>
<a name="yowsup_Set"></a>
<b>Set</b>
<ul>
<li>image &lt;path&gt;<br>
sends an image</li>
<li>send &lt;text&gt;<br>
sends &lt;text&gt;</li>
</ul><br>
<a name="yowsup_Attr"></a>
<b>Attributes</b>
<ul>
<li>cmd<br>
complette commandline to start the yowsup cli client<br>
eg: attr WhatsApp cmd /opt/local/bin/yowsup-cli demos -c /root/config.yowsup --yowsup</li>
<li>accept_from<br>
comma separated list of contacts (numbers) from which messages will be accepted</li>
<li>commandPrefix<br>
not set -> don't accept commands<br>
0 -> don't accept commands<br>
1 -> allow commands, every message is interpreted as a fhem command<br>
anything else -> if the message starts with this prefix then everything after the prefix is taken as the command</li>
<li>allowedCommands<br>
A comma separated list of commands that are allowed from this contact.<br>
If set to an empty list <code>, (i.e. comma only)</code> no commands are accepted.<br>
<b>Note: </b>allowedCommands should work as intended, but no guarantee
can be given that there is no way to circumvent it.</li>
</ul>
</ul>
=end html
=cut

View File

@ -121,6 +121,7 @@ FHEM/31_ENECSYSINV.pm akw http://forum.fhem.de Sonstige
FHEM/31_LightScene.pm justme1968 http://forum.fhem.de Automatisierung
FHEM/32_SYSSTAT.pm justme1968 http://forum.fhem.de Unterstuetzende Dienste
FHEM/32_mailcheck.pm justme1968 http://forum.fhem.de Automatisierung
FHEM/32_yowsup.pm justme1968 http://forum.fhem.de Unterstuetzende Dienste
FHEM/32_withings.pm justme1968 http://forum.fhem.de Sonstiges
FHEM/33_readingsGroup.pm justme1968 http://forum.fhem.de Frontends
FHEM/33_readingsHistory.pm justme1968 http://forum.fhem.de Frontends