mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-03-10 03:06:37 +00:00
inital checkin of Broadlink Module See: https://forum.fhem.de/index.php/topic,71972
git-svn-id: https://svn.fhem.de/fhem/trunk@14361 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
d60b7b674a
commit
0768193de3
765
fhem/FHEM/38_Broadlink.pm
Normal file
765
fhem/FHEM/38_Broadlink.pm
Normal file
@ -0,0 +1,765 @@
|
||||
#original script by https://github.com/mjg59/python-broadlink
|
||||
#some parts by 31_LightScene.pm
|
||||
# $Id$
|
||||
package main;
|
||||
use strict;
|
||||
use warnings;
|
||||
use integer;
|
||||
use Time::Local;
|
||||
use IO::Socket::INET;
|
||||
use IO::Select;
|
||||
|
||||
#use Crypt::CBC;
|
||||
#use Crypt::OpenSSL::AES;
|
||||
#use MIME::Base64;
|
||||
#use Data::Dump qw(dump);
|
||||
|
||||
my $broadlink_hasJSON = 1;
|
||||
my $broadlink_hasDataDumper = 1;
|
||||
my $broadlink_hasCBC = 1;
|
||||
my $broadlink_hasAES = 1;
|
||||
my $broadlink_hasBase64 = 1;
|
||||
|
||||
sub Broadlink_Initialize($) {
|
||||
my ($hash) = @_;
|
||||
|
||||
$hash->{DefFn} = 'Broadlink_Define';
|
||||
$hash->{UndefFn} = 'Broadlink_Undef';
|
||||
$hash->{SetFn} = 'Broadlink_Set';
|
||||
|
||||
$hash->{AttrList} = 'socket_timeout:0.5,1,1.5,2,2.5,3,4,5,10 ' . $readingFnAttributes;
|
||||
eval "use JSON";
|
||||
$broadlink_hasJSON = 0 if($@);
|
||||
|
||||
eval "use Data::Dumper";
|
||||
$broadlink_hasDataDumper = 0 if($@);
|
||||
|
||||
eval "use Crypt::CBC";
|
||||
$broadlink_hasCBC = 0 if($@);
|
||||
|
||||
eval "use Crypt::OpenSSL::AES";
|
||||
$broadlink_hasAES = 0 if($@);
|
||||
|
||||
eval "use MIME::Base64";
|
||||
$broadlink_hasBase64 = 0 if($@);
|
||||
|
||||
}
|
||||
|
||||
sub Broadlink_Define($$) {
|
||||
my ($hash, $def) = @_;
|
||||
my @param = split('[ \t]+', $def);
|
||||
|
||||
return "install Crypt::CBC to use Broadlink" if( !$broadlink_hasCBC);
|
||||
return "install Crypt::OpenSSL::AES to use Broadlink" if( !$broadlink_hasAES);
|
||||
return "install MIME::Base64 to use Broadlink" if( !$broadlink_hasBase64);
|
||||
return "install JSON (or Data::Dumper) to use Broadlink" if( !$broadlink_hasJSON && !$broadlink_hasDataDumper );
|
||||
|
||||
if(int(@param) <= 4) {
|
||||
return "wrong syntax: define <name> Broadlink <connection=ip> <mac=xx:xx:xx:xx:xx> <optional type=rmpro or sp3>";
|
||||
}
|
||||
|
||||
$hash->{ip} = $param[2];
|
||||
$hash->{mac} = $param[3];
|
||||
$hash->{devtype} = $param[4];
|
||||
$hash->{'.key'} = pack('C*', 0x09, 0x76, 0x28, 0x34, 0x3f, 0xe9, 0x9e, 0x23, 0x76, 0x5c, 0x15, 0x13, 0xac, 0xcf, 0x8b, 0x02);
|
||||
$hash->{'.iv'} = pack('C*', 0x56, 0x2e, 0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28, 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58);
|
||||
$hash->{'.id'} = pack('C*', 0, 0, 0, 0);
|
||||
$hash->{counter} = 1;
|
||||
$hash->{isAuthenticated} = 0;
|
||||
if ($hash->{devtype} eq 'sp3') { #steckdose
|
||||
Broadlink_auth($hash);
|
||||
if ($hash->{isAuthenticated} != 0) {
|
||||
Broadlink_sp3_getStatus($hash, 1);
|
||||
}
|
||||
} else {
|
||||
$hash->{commandList} = ();
|
||||
Broadlink_Load($hash);
|
||||
}
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub Broadlink_Undef($$) {
|
||||
my ($hash, $arg) = @_;
|
||||
# nothing to do
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub Broadlink_Get($@) {
|
||||
my ($hash, @param) = @_;
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub Broadlink_Set(@) {
|
||||
my ($hash, $name, $cmd, @args) = @_;
|
||||
if ($hash->{devtype} eq 'sp3') { #steckdose
|
||||
if ($cmd eq 'on') {
|
||||
Broadlink_auth($hash);
|
||||
if ($hash->{isAuthenticated} != 0) {
|
||||
Broadlink_sp3_setPower($hash, 1);
|
||||
}
|
||||
return undef;
|
||||
} elsif ($cmd eq 'off') {
|
||||
Broadlink_auth($hash);
|
||||
if ($hash->{isAuthenticated} != 0) {
|
||||
Broadlink_sp3_setPower($hash, 0);
|
||||
}
|
||||
return undef;
|
||||
} elsif ($cmd eq 'toggle') {
|
||||
Broadlink_auth($hash);
|
||||
if ($hash->{isAuthenticated} != 0) {
|
||||
if ($hash->{STATE} eq 'unknown') {
|
||||
Broadlink_sp3_getStatus($hash);
|
||||
}
|
||||
if ($hash->{STATE} eq 'on') {
|
||||
Broadlink_sp3_setPower($hash, 0);
|
||||
} else {
|
||||
Broadlink_sp3_setPower($hash, 1);
|
||||
}
|
||||
}
|
||||
return undef;
|
||||
} elsif ($cmd eq 'getStatus') {
|
||||
Broadlink_auth($hash);
|
||||
if ($hash->{isAuthenticated} != 0) {
|
||||
Broadlink_sp3_getStatus($hash);
|
||||
}
|
||||
return undef;
|
||||
} else {
|
||||
return "Unknown argument $cmd, choose one of on off toggle getStatus";
|
||||
}
|
||||
return "$cmd. Try to get it.";
|
||||
} else { #rmpro rmmini etc.
|
||||
if ($cmd eq 'recordNewCommand') {
|
||||
Broadlink_auth($hash);
|
||||
my $cmdname = $args[0];
|
||||
if ($cmdname eq "") {
|
||||
return "Please specify commandname to record set <dev> recordNewCommand <name of the command>";
|
||||
}
|
||||
#if ($cmdname =~ /#|'|"|\/|\\|,/) {
|
||||
# return "it is not allowed to use #,',\",\/,\\ or comma in commandname";
|
||||
#}
|
||||
if ($cmdname =~ /^[A-Z_a-z0-9\+\-]+$/) {
|
||||
$hash->{STATE} = "learning new command";
|
||||
if ($hash->{isAuthenticated} != 0) {
|
||||
Broadlink_enterLearning($hash, $cmdname);
|
||||
}
|
||||
} else {
|
||||
return "only A-Z, a-z, 0-9, _, +, - are allowed in commandname";
|
||||
}
|
||||
return undef;
|
||||
} elsif ($cmd eq 'commandSend') {
|
||||
Broadlink_auth($hash);
|
||||
my $cmdname = $args[0];
|
||||
if(!$hash->{commandList}{$cmdname}) {
|
||||
return "Unknown command $cmdname, choose an existing one or record a new one";
|
||||
}
|
||||
$hash->{STATE} = "send command:" . $cmdname;
|
||||
if ($hash->{isAuthenticated} != 0) {
|
||||
Broadlink_send_data($hash, decode_base64($hash->{commandList}{$cmdname}), $cmdname);
|
||||
}
|
||||
return undef;
|
||||
} elsif ($cmd eq 'rename') {
|
||||
my $cmdname = $args[0];
|
||||
my $newCmdname = $args[1];
|
||||
if ($cmdname eq "" or $newCmdname eq "") {
|
||||
return "Command wrong use set <dev> rename <oldname> <newname>";
|
||||
}
|
||||
if(!$hash->{commandList}{$cmdname}) {
|
||||
return "Unknown command $cmdname, choose an existing one";
|
||||
}
|
||||
if ($newCmdname =~ /^[A-Z_a-z0-9\+\-]+$/) {
|
||||
Broadlink_Load($hash);
|
||||
$hash->{commandList}{$newCmdname} = $hash->{commandList}{$cmdname};
|
||||
delete($hash->{commandList}{$cmdname});
|
||||
Broadlink_Save($hash);
|
||||
} else {
|
||||
return "only A-Z, a-z, 0-9, _, +, - are allowed in commandname";
|
||||
}
|
||||
return undef;
|
||||
} elsif ($cmd eq 'remove') {
|
||||
my $cmdname = $args[0];
|
||||
if(!$hash->{commandList}{$cmdname}) {
|
||||
return "Unknown command $cmdname, choose an existing one";
|
||||
}
|
||||
Broadlink_Load($hash);
|
||||
delete($hash->{commandList}{$cmdname});
|
||||
Broadlink_Save($hash);
|
||||
return undef;
|
||||
} else {
|
||||
#sort with ignore case
|
||||
my $commandList = join(",", sort {
|
||||
lc $a cmp lc $b
|
||||
|| $a cmp $b
|
||||
} keys %{$hash->{commandList}});
|
||||
#return "Unknown argument $cmd, choose one of learnNewCommand sendCommand sendCommandBase64 sendCommandHex createCommandBase64 createCommandHex";
|
||||
return "Unknown argument $cmd, choose one of recordNewCommand rename remove:" . $commandList . " commandSend:". $commandList;
|
||||
}
|
||||
return "$cmd. Try to get it.";
|
||||
}
|
||||
}
|
||||
|
||||
sub Broadlink_send_data(@) {
|
||||
my ($hash, $dataToSend, $cmdname) = @_;
|
||||
my @broadlink_payload = ((0) x 4);
|
||||
$broadlink_payload[0] = 2;
|
||||
my @values = split(//,$dataToSend);
|
||||
foreach my $val (@values) {
|
||||
push @broadlink_payload, unpack("C*", $val);
|
||||
}
|
||||
my $msg = "Try to send a command: " . $cmdname;
|
||||
Log3 $hash->{NAME}, 5, $msg;
|
||||
my $response = Broadlink_send_packet($hash, 0x6a, @broadlink_payload);
|
||||
if (length($response) > 0 && $response ne "xxx") {
|
||||
readingsSingleUpdate ( $hash, "lastCommandSend", $cmdname, 1 );
|
||||
my $msg = $cmdname ." send";
|
||||
Log3 $hash->{NAME}, 5, $msg;
|
||||
} else {
|
||||
readingsSingleUpdate ( $hash, "connectionErrorOn", "sendCommand: " . $cmdname, 1 );
|
||||
my $msg = $cmdname . " command send failed - device not connected?";
|
||||
Log3 $hash->{NAME}, 4, $msg;
|
||||
$hash->{STATE} = $msg;
|
||||
}
|
||||
}
|
||||
|
||||
sub Broadlink_check_data(@) {
|
||||
my ($hash) = @_;
|
||||
my @broadlink_payload = ((0) x 16);
|
||||
$broadlink_payload[0] = 4;
|
||||
my $msg = "check for new command";
|
||||
Log3 $hash->{NAME}, 5, $msg;
|
||||
my $data = Broadlink_send_packet($hash, 0x6a, @broadlink_payload);
|
||||
#length must be bigger than 0x38, if not, cant get substring with data
|
||||
if (length($data) > 0x38 && $data ne "xxx") {
|
||||
my $err = unpack("C*", substr($data, 0x22, 1)) | (unpack("C*", substr($data, 0x23, 1)) << 8);
|
||||
if ($err == 0) {
|
||||
my $msg = "new command found";
|
||||
Log3 $hash->{NAME}, 5, $msg;
|
||||
my $enc_payload = substr($data, 0x38);
|
||||
my $cipher = Broadlink_getCipher($hash);
|
||||
my $decodedData = $cipher->decrypt($enc_payload);
|
||||
$hash->{STATE} = "new Command learned: " . $hash->{'.newcommandname'};
|
||||
readingsSingleUpdate ( $hash, "lastRecordedCommand", $hash->{'.newcommandname'}, 1 );
|
||||
#frist load it again, if more than one device is defined
|
||||
Broadlink_Load($hash);
|
||||
$hash->{commandList}{$hash->{'.newcommandname'}} = encode_base64(substr($decodedData, 4));
|
||||
Broadlink_Save($hash);
|
||||
return substr($decodedData, 4);
|
||||
} else {
|
||||
my $msg = "Error receiving command";
|
||||
Log3 $hash->{NAME}, 5, $msg;
|
||||
}
|
||||
} else {
|
||||
my $msg = "no new command data found - data length: " . length($data);
|
||||
Log3 $hash->{NAME}, 4, $msg;
|
||||
$hash->{STATE} = $msg;
|
||||
}
|
||||
$hash->{'.broadlink_checkCommands'}++;
|
||||
if ($hash->{'.broadlink_checkCommands'} < 15) {
|
||||
my $msg = "no command recorded. retry count:" . $hash->{'.broadlink_checkCommands'};
|
||||
Log3 $hash->{NAME}, 5, $msg;
|
||||
InternalTimer(gettimeofday()+2, "Broadlink_check_data", $hash);
|
||||
} else {
|
||||
my $msg = "no command recorded even after a lot of retries. Try to learn again";
|
||||
Log3 $hash->{NAME}, 3, $msg;
|
||||
$hash->{STATE} = $msg;
|
||||
readingsSingleUpdate ( $hash, "lastFailedRecordedCommand", $hash->{'.newcommandname'}, 1 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub Broadlink_sp3_getStatus(@) {
|
||||
my ($hash) = @_;
|
||||
my @broadlink_payload = ((0) x 16);
|
||||
$broadlink_payload[0] = 1;
|
||||
my $msg = "sp3_status request";
|
||||
Log3 $hash->{NAME}, 5, $msg;
|
||||
my $data = Broadlink_send_packet($hash, 0x6a, @broadlink_payload);
|
||||
#length must be bigger than 0x38, if not, cant get substring with data
|
||||
if (length($data) > 0x38 && $data ne "xxx") {
|
||||
my $err = unpack("C*", substr($data, 0x22, 1)) | (unpack("C*", substr($data, 0x23, 1)) << 8);
|
||||
if ($err == 0) {
|
||||
my $msg = "sp3 receiving status - data length: " . length($data);
|
||||
Log3 $hash->{NAME}, 1, $msg;
|
||||
my $enc_payload = substr($data, 0x38);
|
||||
my $cipher = Broadlink_getCipher($hash);
|
||||
my $decodedData = $cipher->decrypt($enc_payload);
|
||||
if (unpack("C*", substr($decodedData, 4, 1)) eq 0) {
|
||||
$hash->{STATE} = "off";
|
||||
} else {
|
||||
$hash->{STATE} = "on";
|
||||
}
|
||||
} else {
|
||||
my $msg = "Error receiving status";
|
||||
Log3 $hash->{NAME}, 4, $msg;
|
||||
$hash->{STATE} = "unknown";
|
||||
readingsSingleUpdate ( $hash, "connectionErrorOn", "geStatusWithData", 1 );
|
||||
}
|
||||
} else {
|
||||
my $msg = "no new command data found - data length: " . length($data);
|
||||
Log3 $hash->{NAME}, 4, $msg;
|
||||
$hash->{STATE} = "unknown";
|
||||
readingsSingleUpdate ( $hash, "connectionErrorOn", "geStatus", 1 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub Broadlink_sp3_setPower(@) {
|
||||
my ($hash, $on) = @_;
|
||||
my @broadlink_payload = ((0) x 16);
|
||||
$broadlink_payload[0] = 2;
|
||||
if ($on == 1) {
|
||||
$broadlink_payload[4] = 1;
|
||||
} else {
|
||||
$broadlink_payload[4] = 0;
|
||||
}
|
||||
my $msg = "sp3_status request";
|
||||
Log3 $hash->{NAME}, 5, $msg;
|
||||
my $data = Broadlink_send_packet($hash, 0x6a, @broadlink_payload);
|
||||
if (length($data) > 0 && $data ne "xxx") {
|
||||
if ($on == 1) {
|
||||
$hash->{STATE} = "on";
|
||||
} else {
|
||||
$hash->{STATE} = "off";
|
||||
}
|
||||
} else {
|
||||
readingsSingleUpdate ( $hash, "connectionErrorOn", "powerChange", 1 );
|
||||
my $msg = "powerChange - device not connected?";
|
||||
Log3 $hash->{NAME}, 4, $msg;
|
||||
$hash->{STATE} = "unkown";
|
||||
}
|
||||
}
|
||||
|
||||
sub Broadlink_enterLearning(@) {
|
||||
my ($hash, $cmdname) = @_;
|
||||
my @broadlink_payload = ((0) x 16);
|
||||
$broadlink_payload[0] = 3;
|
||||
my $msg = "learn new commadn for " . $cmdname;
|
||||
Log3 $hash->{NAME}, 5, $msg;
|
||||
my $data = Broadlink_send_packet($hash, 0x6a, @broadlink_payload);
|
||||
if (length($data) > 0 && $data ne "xxx") {
|
||||
$hash->{'.broadlink_checkCommands'} = 0;
|
||||
$hash->{'.newcommandname'} = $cmdname;
|
||||
my $msg = "start polling for " . $cmdname;
|
||||
Log3 $hash->{NAME}, 5, $msg;
|
||||
InternalTimer(gettimeofday()+2, "Broadlink_check_data", $hash);
|
||||
} else {
|
||||
readingsSingleUpdate ( $hash, "connectionErrorOn", "enterLearning", 1 );
|
||||
my $msg = "command learn failed - device not connected?";
|
||||
Log3 $hash->{NAME}, 4, $msg;
|
||||
$hash->{STATE} = $msg;
|
||||
}
|
||||
}
|
||||
|
||||
sub Broadlink_auth(@) {
|
||||
my ($hash) = @_;
|
||||
#never authenticate again, if not needed
|
||||
if ($hash->{isAuthenticated} == 0) {
|
||||
my @broadlink_payload = ((0) x 80);
|
||||
$broadlink_payload[0x04] = 0x31;
|
||||
$broadlink_payload[0x05] = 0x31;
|
||||
$broadlink_payload[0x06] = 0x31;
|
||||
$broadlink_payload[0x07] = 0x31;
|
||||
$broadlink_payload[0x08] = 0x31;
|
||||
$broadlink_payload[0x09] = 0x31;
|
||||
$broadlink_payload[0x0a] = 0x31;
|
||||
$broadlink_payload[0x0b] = 0x31;
|
||||
$broadlink_payload[0x0c] = 0x31;
|
||||
$broadlink_payload[0x0d] = 0x31;
|
||||
$broadlink_payload[0x0e] = 0x31;
|
||||
$broadlink_payload[0x0f] = 0x31;
|
||||
$broadlink_payload[0x10] = 0x31;
|
||||
$broadlink_payload[0x11] = 0x31;
|
||||
$broadlink_payload[0x12] = 0x31;
|
||||
$broadlink_payload[0x1e] = 0x01;
|
||||
$broadlink_payload[0x2d] = 0x01;
|
||||
$broadlink_payload[0x30] = ord('T');
|
||||
$broadlink_payload[0x31] = ord('e');
|
||||
$broadlink_payload[0x32] = ord('s');
|
||||
$broadlink_payload[0x33] = ord('t');
|
||||
$broadlink_payload[0x34] = ord(' ');
|
||||
$broadlink_payload[0x35] = ord(' ');
|
||||
$broadlink_payload[0x36] = ord('1');
|
||||
|
||||
my $msg = "try to authenticate";
|
||||
Log3 $hash->{NAME}, 5, $msg;
|
||||
my $response = Broadlink_send_packet($hash, 0x65, @broadlink_payload);
|
||||
if (length($response) > 0x38 && $response ne "xxx") {
|
||||
my $enc_payload = substr($response, 0x38);
|
||||
my $cipher = Broadlink_getCipher($hash);
|
||||
my $broadlink_payload = $cipher->decrypt($enc_payload);
|
||||
#authentication worked
|
||||
$hash->{'.key'} = substr($broadlink_payload, 0x04, 16);
|
||||
$hash->{'.id'} = substr($broadlink_payload, 0, 4);
|
||||
$hash->{isAuthenticated} = 1;
|
||||
} else {
|
||||
readingsSingleUpdate ( $hash, "lastAuthenticationFailed", "", 1 );
|
||||
my $msg = "authentication failed - device not connected? - response length: " . length($response);
|
||||
Log3 $hash->{NAME}, 4, $msg;
|
||||
$hash->{STATE} = $msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub Broadlink_getCipher(@) {
|
||||
my ($hash) = @_;
|
||||
return Crypt::CBC->new(
|
||||
-key => $hash->{'.key'},
|
||||
-cipher => "Crypt::OpenSSL::AES",
|
||||
-header => "none",
|
||||
-iv => $hash->{'.iv'},
|
||||
-literal_key => 1,
|
||||
-keysize => 16,
|
||||
-padding => 'space'
|
||||
);
|
||||
}
|
||||
|
||||
sub Broadlink_send_packet(@) {
|
||||
my ($hash,$command,@broadlink_payload) = @_;
|
||||
|
||||
#prepare header of packet
|
||||
$hash->{counter} = ($hash->{counter} + 1) & 0xffff;
|
||||
|
||||
my @broadlink_id = split(//,$hash->{'.id'});
|
||||
my @broadlink_mac = split ':', $hash->{mac};
|
||||
|
||||
my @packet = (0) x 56;
|
||||
$packet[0x00] = 0x5a;
|
||||
$packet[0x01] = 0xa5;
|
||||
$packet[0x02] = 0xaa;
|
||||
$packet[0x03] = 0x55;
|
||||
$packet[0x04] = 0x5a;
|
||||
$packet[0x05] = 0xa5;
|
||||
$packet[0x06] = 0xaa;
|
||||
$packet[0x07] = 0x55;
|
||||
$packet[0x24] = 0x2a;
|
||||
$packet[0x25] = 0x27;
|
||||
$packet[0x26] = $command;
|
||||
$packet[0x28] = $hash->{counter} & 0xff;
|
||||
$packet[0x29] = $hash->{counter} >> 8;
|
||||
$packet[0x2a] = unpack('H', $broadlink_mac[0]);
|
||||
$packet[0x2b] = unpack('H', $broadlink_mac[1]);
|
||||
$packet[0x2c] = unpack('H', $broadlink_mac[2]);
|
||||
$packet[0x2d] = unpack('H', $broadlink_mac[3]);
|
||||
$packet[0x2e] = unpack('H', $broadlink_mac[4]);
|
||||
$packet[0x2f] = unpack('H', $broadlink_mac[5]);
|
||||
|
||||
$packet[0x30] = unpack('C', $broadlink_id[0]);
|
||||
$packet[0x31] = unpack('C', $broadlink_id[1]);
|
||||
$packet[0x32] = unpack('C', $broadlink_id[2]);
|
||||
$packet[0x33] = unpack('C', $broadlink_id[3]);
|
||||
|
||||
#calculate payload checksum of original data
|
||||
my $checksum = 0xbeaf;
|
||||
my $arrSize = @broadlink_payload;
|
||||
for(my $i = 0; $i < $arrSize; $i++) {
|
||||
$checksum += $broadlink_payload[$i];
|
||||
$checksum = $checksum & 0xffff;
|
||||
}
|
||||
#put the checksum of payload in the header info
|
||||
$packet[0x34] = $checksum & 0xff;
|
||||
$packet[0x35] = $checksum >> 8;
|
||||
|
||||
#crypt payload
|
||||
my $cipher = Broadlink_getCipher($hash);
|
||||
my $payloadCrypt = $cipher->encrypt(pack('C*', @broadlink_payload));
|
||||
|
||||
#add the crypted data to packet
|
||||
my @values = split(//,$payloadCrypt);
|
||||
foreach my $val (@values) {
|
||||
push @packet, unpack("C*", $val);
|
||||
}
|
||||
|
||||
#create checksum of whole packet
|
||||
$checksum = 0xbeaf;
|
||||
$arrSize = @packet;
|
||||
for(my $i = 0; $i < $arrSize; $i++) {
|
||||
$checksum += $packet[$i];
|
||||
$checksum = $checksum & 0xffff;
|
||||
}
|
||||
#put the checksum of whole packet in the header info
|
||||
$packet[0x20] = $checksum & 0xff;
|
||||
$packet[0x21] = $checksum >> 8;
|
||||
|
||||
#errorvalue if no data received
|
||||
my $data = "xxx";
|
||||
my $timeout = AttrVal($hash->{NAME}, 'socket_timeout', 3.0);
|
||||
eval {
|
||||
local $SIG{ALRM} = sub { die 'Timed Out'; };
|
||||
alarm $timeout;
|
||||
|
||||
#send udp packet
|
||||
my $socket = IO::Socket::INET->new(
|
||||
Proto => 'udp',
|
||||
PeerAddr => $hash->{ip},
|
||||
PeerPort => '80',
|
||||
ReuseAddr => 1,
|
||||
Timeout => $timeout,
|
||||
#Blocking => 0
|
||||
) or Log3 $hash->{NAME}, 5, "Problem with socket";
|
||||
|
||||
my $select = IO::Select->new($socket) if $socket;
|
||||
|
||||
|
||||
#$socket->autoflush;
|
||||
$socket->send(pack('C*',@packet));
|
||||
#IO::Select->select($select, undef, undef, 3);
|
||||
if ($select->can_read($timeout)) {
|
||||
$socket->recv($data, 1024);
|
||||
} else {
|
||||
Log3 $hash->{NAME}, 5, "can't read";
|
||||
}
|
||||
$socket->close();
|
||||
alarm 0;
|
||||
};
|
||||
alarm 0; # race condition protection
|
||||
Log3 $hash->{NAME}, 3, 'Error Timout' if ( $@ && $@ =~ /Timed Out/ );
|
||||
Log3 $hash->{NAME}, 3, "Error: Eval corrupted: $@" if $@;
|
||||
Log3 $hash->{NAME}, 5, length($data) . " bytes received from socket";
|
||||
return $data;
|
||||
}
|
||||
|
||||
#lightscene Copy
|
||||
sub Broadlink_statefileName() {
|
||||
my $statefile = $attr{global}{statefile};
|
||||
$statefile = substr $statefile,0,rindex($statefile,'/')+1;
|
||||
return $statefile ."broadlink.save" if( $broadlink_hasJSON );
|
||||
return $statefile ."broadlink.dd.save" if( $broadlink_hasDataDumper );
|
||||
}
|
||||
|
||||
sub Broadlink_Save(@) {
|
||||
my ($hash) = @_;
|
||||
my $time_now = TimeNow();
|
||||
|
||||
return "No statefile specified" if(!$attr{global}{statefile});
|
||||
my $statefile = Broadlink_statefileName();
|
||||
|
||||
my $commandList = $hash->{commandList};
|
||||
|
||||
if(open(FH, ">$statefile")) {
|
||||
my $t = localtime;
|
||||
print FH "#$t\n";
|
||||
|
||||
if( $broadlink_hasJSON ) {
|
||||
print FH encode_json($commandList) if( defined($commandList) );
|
||||
} elsif( $broadlink_hasDataDumper ) {
|
||||
my $dumper = Data::Dumper->new([]);
|
||||
$dumper->Terse(1);
|
||||
|
||||
$dumper->Values([$commandList]);
|
||||
print FH $dumper->Dump;
|
||||
}
|
||||
|
||||
close(FH);
|
||||
} else {
|
||||
|
||||
my $msg = "Broadlink_Save: Cannot open $statefile: $!";
|
||||
Log3 $hash->{NAME}, 1, $msg;
|
||||
}
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub Broadlink_Load(@) {
|
||||
my ($hash) = @_;
|
||||
|
||||
return "No statefile specified" if(!$attr{global}{statefile});
|
||||
my $statefile = Broadlink_statefileName();
|
||||
|
||||
if(open(FH, "<$statefile")) {
|
||||
my $encoded;
|
||||
while (my $line = <FH>) {
|
||||
chomp $line;
|
||||
next if($line =~ m/^#.*$/);
|
||||
$encoded .= $line;
|
||||
}
|
||||
close(FH);
|
||||
|
||||
return if( !defined($encoded) );
|
||||
|
||||
my $decoded;
|
||||
if( $broadlink_hasJSON ) {
|
||||
$decoded = decode_json( $encoded );
|
||||
} elsif( $broadlink_hasDataDumper ) {
|
||||
$decoded = eval $encoded;
|
||||
}
|
||||
$hash->{commandList} = $decoded;
|
||||
} else {
|
||||
my $msg = "Broadlink_Load: Cannot open $statefile: $!";
|
||||
Log3 $hash->{NAME}, 1, $msg;
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
=pod
|
||||
=item device
|
||||
=item summary implements a connection to Broadlink devices
|
||||
=item summary_DE implementiert die Verbindung zu Broadlink Geräten
|
||||
=begin html
|
||||
|
||||
<a name="Broadlink"></a>
|
||||
<h3>Broadlink</h3>
|
||||
<ul>
|
||||
<i>Broadlink</i> implements a connection to Broadlink devices - currently tested with Broadlink RM Pro, which is able to send IR and 433MHz commands. It is also able to record this commands.
|
||||
<br>
|
||||
It requires AES encryption please install on Windows:<br>
|
||||
<code>ppm install Crypt-CBC</code><br>
|
||||
<code>ppm install Crypt-OpenSSL-AES</code><br><br>
|
||||
or Linux/Raspberry:
|
||||
<code>sudo apt-get install libcrypt-cbc-perl</code><br>
|
||||
<code>sudo apt-get install libcrypt-rijndael-perl</code><br>
|
||||
<code>sudo cpan Crypt/OpenSSL/AES.pm</code><br>
|
||||
<br><br>
|
||||
<a name="Broadlinkdefine"></a>
|
||||
<b>Define</b>
|
||||
<ul>
|
||||
<code>define <name> Broadlink <ip/host> <mac> <type=rmpro or sp3></code>
|
||||
<br><br>
|
||||
Example: <code>define broadlinkWZ Broadlink 10.23.11.85 34:EA:34:F4:77:7B rmpro</code>
|
||||
<br><br>
|
||||
The <i>mac</i> of the device have to be set in format: xx:xx:xx:xx:xx<br>
|
||||
The type is in current development state optional.
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<a name="Broadlinkset"></a>
|
||||
<b>Set for rmpro</b><br>
|
||||
<ul>
|
||||
<li><code>set <name> <commandSend> <command name></code>
|
||||
<br><br>
|
||||
Send a previous recorded command.
|
||||
</li>
|
||||
<li><code>set <name> recordNewCommand <command name></code>
|
||||
<br><br>
|
||||
Records a new command. You have to specify a commandname
|
||||
</li>
|
||||
<li>
|
||||
<code>set <name> remove <command name></code>
|
||||
<br><br>
|
||||
Removes a recored command.
|
||||
</li>
|
||||
<li>
|
||||
<code>set <name> rename <old command name> <new command name></code>
|
||||
<br><br>
|
||||
Renames a recored command.
|
||||
</li>
|
||||
</ul>
|
||||
<br>
|
||||
<b>Set for sp3</b><br>
|
||||
<ul>
|
||||
<li><code>set <name> on</code>
|
||||
<br><br>
|
||||
Set the device on
|
||||
</li>
|
||||
<li><code>set <name> off</code>
|
||||
<br><br>
|
||||
Set the device off
|
||||
</li>
|
||||
<li><code>set <name> toggle</code>
|
||||
<br><br>
|
||||
Toggle the device on and off
|
||||
</li>
|
||||
<li><code>set <name> getStatus</code>
|
||||
<br><br>
|
||||
Get the device on/off status
|
||||
</li>
|
||||
</ul>
|
||||
<br>
|
||||
<a name="Broadlinkattr"></a>
|
||||
<b>Attributes for all Broadlink Devices</b><br>
|
||||
<ul>
|
||||
<li><code>socket_timeout</code>
|
||||
<br><br>
|
||||
sets a timeout for the device communication
|
||||
</li>
|
||||
</ul>
|
||||
<br>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
=begin html_DE
|
||||
|
||||
<a name="Broadlink"></a>
|
||||
<h3>Broadlink</h3>
|
||||
<ul>
|
||||
<i>Broadlink</i> implementiert die Verbindung zu Broadlink Geräten - aktuell mit Broadlink RM Pro, welcher sowohl Infrarot als auch 433MHz aufnehmen und anschließend versenden kann.
|
||||
<br>
|
||||
Das Modul benötigt AES-Verschlüsslung.<br>
|
||||
In Windows installiert man die Untersützung mit:<br>
|
||||
<code>ppm install Crypt-CBC</code><br>
|
||||
<code>ppm install Crypt-OpenSSL-AES</code><br><br>
|
||||
Auf Linux/Raspberry:
|
||||
<code>sudo apt-get install libcrypt-cbc-perl</code><br>
|
||||
<code>sudo apt-get install libcrypt-rijndael-perl</code><br>
|
||||
<code>sudo cpan Crypt/OpenSSL/AES.pm</code><br>
|
||||
<br><br>
|
||||
<a name="Broadlinkdefine"></a>
|
||||
<b>Define</b>
|
||||
<ul>
|
||||
<code>define <name> Broadlink <ip/host> <mac> <type=rmpro or sp3></code>
|
||||
<br><br>
|
||||
Beispiel: <code>define broadlinkWZ Broadlink 10.23.11.85 34:EA:34:F4:77:7B rmpro</code>
|
||||
<br><br>
|
||||
Die <i>mac</i>-Adresse des Gerätes muss im folgenden Format eingegeben werden: xx:xx:xx:xx:xx<br>
|
||||
Der Typ <i>sp3</i> wird für schaltbare Steckdosen genutzt. <i>rmpro</i> für alle anderen Geräte.
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<a name="Broadlinkset"></a>
|
||||
<b>Set für rmpro</b><br>
|
||||
<ul>
|
||||
<li><code>set <name> <commandSend> <command name></code>
|
||||
<br><br>
|
||||
Sendet ein vorher aufgenommenen Befehl
|
||||
</li>
|
||||
<li><code>set <name> recordNewCommand <command name></code>
|
||||
<br><br>
|
||||
Nimmt ein neuen Befehl auf. Man muss einen Befehlnamen angeben.
|
||||
</li>
|
||||
<li>
|
||||
<code>set <name> remove <command name></code>
|
||||
<br><br>
|
||||
Löscht einen vorher aufgezeichneten Befehl.
|
||||
</li>
|
||||
<li>
|
||||
<code>set <name> rename <old command name> <new command name></code>
|
||||
<br><br>
|
||||
Benennt einen vorher aufgezeichneten Befehl um.
|
||||
</li>
|
||||
</ul>
|
||||
<br>
|
||||
<b>Set für sp3</b><br>
|
||||
<ul>
|
||||
<li><code>set <name> on</code>
|
||||
<br><br>
|
||||
Schaltet das Gerät an.
|
||||
</li>
|
||||
<li><code>set <name> off</code>
|
||||
<br><br>
|
||||
Schaltet das Gerät aus.
|
||||
</li>
|
||||
<li><code>set <name> toggle</code>
|
||||
<br><br>
|
||||
Schaltet das Gerät entweder ein oder aus.
|
||||
</li>
|
||||
<li><code>set <name> getStatus</code>
|
||||
<br><br>
|
||||
Ermittelt den aktuellen Status des Gerätes.
|
||||
</li>
|
||||
</ul>
|
||||
<br>
|
||||
<a name="Broadlinkattr"></a>
|
||||
<b>Attribute für alle Broadlink Gräte</b><br>
|
||||
<ul>
|
||||
<li><code>socket_timeout</code>
|
||||
<br><br>
|
||||
Setzt den Timeout für die Gerätekommunikation.
|
||||
</li>
|
||||
</ul>
|
||||
<br>
|
||||
</ul>
|
||||
|
||||
=end html_DE
|
||||
=cut
|
Loading…
x
Reference in New Issue
Block a user