2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 18:59:33 +00:00
fhem-mirror/fhem/contrib/JeeStuff/00_JeeLink.pm
rudolfkoenig c03cffa773 JeeStuff von Parix
git-svn-id: https://svn.fhem.de/fhem/trunk@950 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2011-07-19 09:31:20 +00:00

466 lines
15 KiB
Perl

################################################################################
# FHEM-Modul see www.fhem.de
# 00_JeeLink.pm
# Modul to use a JeeLink with RF12DEMO as FHEM-IO-Device
#
# Usage: define <Name> JeeLink </dev/...> NodeID
################################################################################
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
################################################################################
# Autor: Axel Rieger
# Version: 1.0
# Datum: 07.2011
# Kontakt: fhem [bei] anax [punkt] info
################################################################################
package main;
use strict;
use warnings;
use Data::Dumper;
use vars qw(%defs);
use vars qw(%attr);
use vars qw(%data);
use vars qw(%modules);
sub JeeLink_Initialize($);
sub JEE_Define($$);
sub JEE_CloseDev($);
sub JEE_OpenDev($$);
sub JEE_Ready($);
sub JEE_Read($);
sub JEE_Set($);
################################################################################
sub JeeLink_Initialize($)
{
my ($hash) = @_;
# Provider
$hash->{ReadFn} = "JEE_Read";
$hash->{ReadyFn} = "JEE_Ready";
$hash->{SetFn} = "JEE_Set";
$hash->{WriteFn} = "JEE_Write";
$hash->{Clients} = ":JSN:JME:JPU";
$hash->{WriteFn} = "JEE_Write";
my %mc = (
"1:JSN" => "^JSN",
"2:JME" => "^JME",
"3:JPU" => "^JPU");
$hash->{MatchList} = \%mc;
# Normal devices
$hash->{DefFn} = "JEE_Define";
$hash->{AttrList} = "do_not_notify:1,0 dummy:1,0 loglevel:0,1 ";
return undef;
}
################################################################################
sub JEE_Define($$) {
# define JEE0001 JeeLink /dev/tty.usbserial-A600cKlS NodeID
# defs = $a[0] <DEVICE-NAME> $a[1] DEVICE-TYPE $a[2]<Parameter-1->;
my ($hash, $def) = @_;
my @a = split(/\s+/, $def);
my $name = $a[0];
my $dev = $a[2];
my $NodeID = $a[3];
if($dev eq "none") {
Log 1, "$name device is none, commands will be echoed only";
$hash->{TYPE} = 'JeeLink';
$hash->{STATE} = TimeNow() . " Dummy-Device";
$attr{$name}{dummy} = 1;
return undef;
}
JEE_CloseDev($hash);
if($NodeID == 0 || $NodeID > 26 ) {return "JeeLink: NodeID between 1 and 26";}
Log 0, "JEE-Define: Name = $name dev=$dev";
$hash->{DeviceName} = $dev;
$hash->{TYPE} = 'JeeLink';
my $ret = JEE_OpenDev($hash, 0);
my $msg = $NodeID . "i";
$ret = &JEE_IOWrite($hash, $msg);
return undef;
}
################################################################################
sub JEE_Set($){
my ($hash, @a) = @_;
# Log 0, ("JEE-SET: " . Dumper(@_));
my $fields .= "NodeID NetGRP Frequenz LED CollectMode SendMSG BroadcastMSG RAW";
return "Unknown argument $a[1], choose one of ". $fields if($a[1] eq "?");
# a[0] = DeviceName
# a[1] = Command
# Command
# nodeID: <n>i set node ID (standard node ids are 1..26 or 'A'..'Z')
# netGRP: <n>g set network group (RFM12 only allows 212)
# freq -> <n>b set MHz band (4 = 433, 8 = 868, 9 = 915)
# cMode -> <n>c - set collect mode (advanced 1, normally 0)
# bCast -> t - broadcast max-size test packet, with ack
# sendA -> ...,<nn> a - send data packet to node <nn>, with ack
# sendS -> ...,<nn> s - send data packet to node <nn>, no ack
# led -> <n> l - turn activity LED on PB1 on or off
# Remote control commands:
# <hchi>,<hclo>,<addr>,<cmd> f - FS20 command (868 MHz)
# <addr>,<dev>,<on> k - KAKU command (433 MHz)
# Flash storage (JeeLink v2 only):
# d - dump all log markers
# <sh>,<sl>,<t3>,<t2>,<t1>,<t0> r - replay from specified marker
# 123,<bhi>,<blo> e - erase 4K block
# 12,34 w - wipe entire flash memory
my($name, $msg);
$name = $a[0];
# LogLevel
my $ll = 0;
if(defined($attr{$name}{loglevel})) {$ll = $attr{$name}{loglevel};}
Log $ll,"$name/JEE-SET: " . $a[1] . " : " . $a[2];
# @a ge 2
# if(int(@a) ne 3) {return "JeeLink wrong Argument Count";}
$msg = "";
if($a[1] eq "NodeID") {
if($a[2] == 0 || $a[2] > 26 ) {return "JeeLink: NodeID between 1 and 26";}
$msg = $a[2] . "i";}
if($a[1] eq "NetGRP") {
if($a[2] == 0 || $a[2] > 255 ) {return "JeeLink: NetGroup between 1 and 255";}
$msg = $a[2] . "g";}
if($a[1] eq "Frequenz") {
if($a[2] !~ m/433|868|933/) {return "JeeLink: Frquenz setting use 433, 868 or 933";}
my $mhz;
if($a[2] eq "433") {$msg = "4b";}
if($a[2] eq "868") {$msg = "8b";}
if($a[2] eq "915") {$msg = "9b";}
# 4 = 433, 8 = 868, 9 = 915
}
# LED
if($a[1] eq "LED" && lc($a[2]) eq "on") {
$hash->{LED} = "ON";
$msg = "1l";}
if($a[1] eq "LED" && lc($a[2]) eq "off") {
$hash->{LED} = "OFF";
$msg = "0l";}
# CollectMode On or Off
if($a[1] eq "CollectMode" && lc($a[2]) eq "on") {
$hash->{CollectMode} = "ON";
$msg = "1c";}
if($a[2] eq "CollectMode" && lc($a[2]) eq "off") {
$hash->{CollectMode} = "OFF";
$msg = "0c";}
# RF12_MSG to Remote Node with NO ack
# set <NAME> SendMSG NodeID Data
if($a[1] eq "SendMSG") {$msg = $a[2] . "," . $a[3] . "s"};
# RF12- BroadcastMSG
if($a[1] eq "BroadcastMSG") {$msg = "t";}
# RAW
if($a[1] eq "RAW") {$msg = $a[2];}
# Send MSG
Log 0,"JEE-SET->WRITE: $msg";
my $ret = &JEE_IOWrite($hash, $msg);
return undef;
}
################################################################################
sub JEE_CloseDev($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
my $dev = $hash->{DeviceName};
return if(!$dev);
$hash->{USBDev}->close() ;
delete($hash->{USBDev});
delete($selectlist{"$name.$dev"});
delete($readyfnlist{"$name.$dev"});
delete($hash->{FD});
}
################################################################################
sub JEE_OpenDev($$)
{
my ($hash, $reopen) = @_;
my $dev = $hash->{DeviceName};
my $name = $hash->{NAME};
my $po;
$hash->{PARTIAL} = "";
Log 3, "JeeLink opening $name device $dev"
if(!$reopen);
my $baudrate;
($dev, $baudrate) = split("@", $dev);
$baudrate = 57600;
if ($^O=~/Win/) {
require Win32::SerialPort;
$po = new Win32::SerialPort ($dev);
} else {
require Device::SerialPort;
$po = new Device::SerialPort ($dev);
}
if(!$po) {
return undef if($reopen);
Log(3, "Can't open $dev: $!");
$readyfnlist{"$name.$dev"} = $hash;
$hash->{STATE} = "disconnected";
return "";
}
$hash->{USBDev} = $po;
if( $^O =~ /Win/ ) {
$readyfnlist{"$name.$dev"} = $hash;
} else {
$hash->{FD} = $po->FILENO;
delete($readyfnlist{"$name.$dev"});
$selectlist{"$name.$dev"} = $hash;
}
if($baudrate) {
$po->reset_error();
Log 3, "$name: Setting baudrate to $baudrate";
$po->baudrate($baudrate);
$po->databits(8);
$po->parity('none');
$po->stopbits(1);
$po->handshake('none');
}
if($reopen) {
Log 1, "JeeLink $dev reappeared ($name)";
} else {
Log 3, "JeeLink device opened";
}
# Set Defaults
# CollectMode on
my $ret = &JEE_IOWrite($hash, "1c");
# QuietMode on
$ret = &JEE_IOWrite($hash, "1q");
# LED On
$ret = &JEE_IOWrite($hash, "1l");
# Set Frequenz to 868MHz
$ret = &JEE_IOWrite($hash, "8b");
$hash->{STATE}="connected"; # Allow InitDev to set the state
DoTrigger($name, "CONNECTED") if($reopen);
return "Initialized";
}
################################################################################
sub JEE_Ready($)
{
my ($hash) = @_;
return JEE_OpenDev($hash, 1)
if($hash->{STATE} eq "disconnected");
# This is relevant for windows/USB only
my $po = $hash->{USBDev};
my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status;
# Get Config
my $ret = &JEE_Write($hash, "i", undef);
return ($InBytes>0);
}
################################################################################
sub JEE_Read($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
# LogLevel
# Default 4
my $ll = 4;
if(defined($attr{$name}{loglevel})) {
$ll = $attr{$name}{loglevel};
}
my $buf = $hash->{USBDev}->input();
#
# Lets' try again: Some drivers return len(0) on the first read...
if(defined($buf) && length($buf) == 0) {
$buf = $hash->{USBDev}->input();
}
my $jeedata = $hash->{PARTIAL};
$jeedata .= $buf;
##############################################################################
# Arduino/JeeLink
# Prints data to the serial port as human-readable ASCII text followed by
# a carriage return character (ASCII 13, or '\r') and
# a newline character (ASCII 10, or '\n').
# HEX 0D AD \xf0
if($jeedata =~ m/\n$/){
chomp($jeedata);
chop($jeedata);
my $status = substr($jeedata, 0, 2);
Log $ll,("$name/JeeLink RAW:$status -> $jeedata");
if($status =~/^OK/){
Log $ll,("$name/JeeLink Dispatch RAW:$jeedata");
&JEE_DispatchData($jeedata,$name,$ll);
}
elsif($jeedata =~ m/(^.*i)([0-9]{1,2})(\*.g)([0-9]{1,3})(.@.)([0-9]{1,3})(.MHz.*)/) {
JEE_RF12MSG($jeedata,$name,$ll);
}
elsif($jeedata =~/^DF/){JEE_RF12MSG($jeedata,$name,$ll);}
$jeedata = "";
}
if($jeedata =~ m/^\x0A/) {
Log $ll,("$name/JeeLink RAW DEL HEX-0A:$jeedata -> $jeedata");
$jeedata =~ s/\x0A//;
}
$hash->{PARTIAL} = $jeedata;
}
################################################################################
sub JEE_DispatchData($){
my ($rawdata,$name,$ll) = @_;
my @data = split(/\s+/,$rawdata);
my $status = shift(@data);
my $NodeID = shift(@data);
# see http://talk.jeelabs.net/topic/642#post-3622
# The id +32 is what you see when the node requests an ACK.
if($NodeID > 31) {$NodeID = $NodeID - 32;}
Log $ll, "$name JEE-DISP: Status:$status NodeID:$NodeID Data:$rawdata";
# normalize 0 => 00 without NodeID
for(my $i=0;$i<=$#data;$i++){
if(length($data[$i]) == 1) { $data[$i] = "0" . $data[$i]}
}
# SensorData to Dispatch
my ($DispData,$SType,$SPre,@SData,$data_bytes,$slice_end);
for(my $i=0;$i<=$#data;$i++){
# Get Number of DataBytes
$SType = $data[$i];
if(defined($data{JEECONF}{$SType}{DataBytes})){
$data_bytes = $data{JEECONF}{$SType}{DataBytes};
###
$SPre = $data{JEECONF}{$SType}{Prefix};
$i++;
$slice_end = $i + $data_bytes - 1;
@SData = @data[$i..$slice_end];
$DispData = $SPre . " " . $NodeID . " " . $SType . " " . join(" ",@SData);
}
else {
Log $ll, "$name JEE-DISP: -ERROR- SensorType $SType not defined";
return undef;
}
# Dispacth-Data to FHEM-Dispatcher -----------------------------------------
#foreach my $m (sort keys %{$modules{JeeLink}{MatchList}}) {
# my $match = $modules{JeeLink}{MatchList}{$m};
$defs{$name}{"${name}_MSGCNT"}++;
$defs{$name}{"${name}_TIME"} = TimeNow();
$defs{$name}{RAWMSG} = $DispData;
my %addvals = (RAWMSG => $DispData);
my $hash = $defs{$name};
Log $ll,"$name JEE-DISP: SType=$SType -> DispData=$DispData";
my $ret_disp = &Dispatch($hash, $DispData, \%addvals);
#}
# Dispacth-Data to FHEM-Dispatcher -----------------------------------------
# Minimum 2Bytes left
# if((int(@data) - $i) < 2 ) {
# Log $ll,"$name JEE-DISP: 2Byte $i -> " . int(@data);
# $i = int(@data);
# }
#else {$i = $slice_end;}
$i = $slice_end;
}
}
################################################################################
sub JEE_RF12MSG($$$){
my ($rawdata,$name,$ll) = @_;
# ^ i23* g212 @ 868 MHz
# i -> NodeID
# g -> NetGroup
# @ -> 868MHz or 492MHz
Log $ll,("$name/JeeLink RF12MSG: $rawdata");
my($NodeID,$NetGroup,$Freq);
if ( $rawdata =~ m/(^.*i)([0-9]{1,2})(\*.g)([0-9]{1,3})(.@.)([0-9]{1,3})(.MHz.*)/) {
($NodeID,$NetGroup,$Freq) = ($2,$4,$6);
Log $ll,("$name/JeeLink RF12MSG-CONFIG: NodeId:$NodeID NetGroup:$NetGroup Freq:$Freq");
$defs{$name}{RF12_NodeID} = $NodeID;
$defs{$name}{RF12_NetGroup} = $NetGroup;
$defs{$name}{RF12_Frequenz} = $Freq;
}
if($rawdata =~ m/\s+DF/){
Log $ll,("$name/JeeLink RF12MSG-FLASH: $rawdata");
}
return undef;
}
################################################################################
sub JEE_IOWrite() {
my ($hash, $msg,) = @_;
return if(!$hash);
# LogLevel
my $name = $hash->{NAME};
my $ll = 4;
if(defined($attr{$name}{loglevel})) {
$ll = $attr{$name}{loglevel};
}
if(defined($attr{$name}{dummy})){
Log $ll ,"JEE-IOWRITE[DUMMY-MODE]: " . $hash->{NAME} . " $msg";
}
else {
Log $ll ,"JEE-IOWRITE: " . $name . " $msg";
$hash->{USBDev}->write($msg . "\n");
select(undef, undef, undef, 0.001);
}
}
################################################################################
sub JEE_Write() {
my ($hash, $msg1, $msg2) = @_;
# $hash -> Received form Device
# $msg1 -> Message Type ???
# $msg2 -> Data
return if(!$hash);
# LogLevel
my $name = $hash->{NAME};
my $ll = 4;
if(defined($attr{$name}{loglevel})) {
$ll = $attr{$name}{loglevel};
}
Log $ll ,"JEE-WRITE: " . $hash->{NAME} . " MSG-1-: $msg1 MSG-2-: $msg2";
# Default --------------------------------------------------------------------
my $msg = $msg2;
# FS20 -----------------------------------------------------------------------
# JEE-WRITE: JL01 MSG: 04 BTN: 010101 1234 33 00
# FS20.pm
# IOWrite($hash, "04", "010101" . $hash->{XMIT} . $hash->{BTN} . $c);
# MSG-1-: 04 MSG-2-: 01010177770311
# <hchi>,<hclo>,<addr>,<cmd> f - FS20 command (868 MHz)
# substr($jeedata, 0, 2);
my ($hchi,$hclo,$addr,$cmd);
if($msg2 =~ m/^010101/) {
$msg2 =~s/010101//;
Log 0, "JEE-IOWRITE-FS20: $msg2";
$hchi = hex(substr($msg2,0,2));
$hclo = substr($msg2,2,2);
$addr = hex(substr($msg2,4,2));
$cmd = hex(substr($msg2,6,2));
$msg = "$hchi,$hclo,$addr,$cmd f";
Log $ll, "JEE-IOWRITE-FS20: hchi:$hchi hclo:$hclo addr:$addr cmd:$cmd";
$hash->{FS20_LastSend} = TimeNow() . ":" . $msg ;
}
# FS20 -----------------------------------------------------------------------
if(defined($attr{$name}{dummy})){
Log $ll, "JEE_Write[DUMMY-MODE]: >$msg<";
}
else {
# Send Message >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
$hash->{USBDev}->write($msg . "\n");
Log $ll, "JEE_Write >$msg<";
select(undef, undef, undef, 0.001);
# Send Message >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
}
}
################################################################################
1;