| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755 |
- ##############################################
- # $Id: 00_CUL.pm 12983 2017-01-06 13:53:27Z rudolfkoenig $
- package main;
- use strict;
- use warnings;
- use Time::HiRes qw(gettimeofday);
- sub CUL_Attr(@);
- sub CUL_Clear($);
- sub CUL_HandleCurRequest($$);
- sub CUL_HandleWriteQueue($);
- sub CUL_Parse($$$$@);
- sub CUL_Read($);
- sub CUL_ReadAnswer($$$$);
- sub CUL_Ready($);
- sub CUL_Write($$$);
- sub CUL_SimpleWrite(@);
- sub CUL_WriteInit($);
- my %gets = ( # Name, Data to send to the CUL, Regexp for the answer
- "ccconf" => 1,
- "version" => ["V", '^V .*'],
- "raw" => ["", '.*'],
- "uptime" => ["t", '^[0-9A-F]{8}[\r\n]*$' ],
- "fhtbuf" => ["T03", '^[0-9A-F]+[\r\n]*$' ],
- "cmds" => ["?", '.*Use one of( .)*[\r\n]*$' ],
- "credit10ms" => [ "X", '^.. *\d*[\r\n]*$' ],
- );
- my %sets = (
- "reopen" => "",
- "hmPairForSec" => "HomeMatic",
- "hmPairSerial" => "HomeMatic",
- "raw" => "",
- "freq" => "SlowRF",
- "bWidth" => "SlowRF",
- "rAmpl" => "SlowRF",
- "sens" => "SlowRF",
- "led" => "",
- "patable" => "",
- "ITClock" => "SlowRF"
- );
- my @ampllist = (24, 27, 30, 33, 36, 38, 40, 42); # rAmpl(dB)
- my $sccMods = "STACKABLE_CC:TSSTACKED"; # for noansi
- my $culNameRe = "^(CUL|TSCUL)\$";
- my $clientsSlowRF = ":FS20:FHT.*:KS300:USF1000:BS:HMS: ".
- ":CUL_EM:CUL_WS:CUL_FHTTK:CUL_HOERMANN: ".
- ":ESA2000:CUL_IR:CUL_TX:Revolt:IT:UNIRoll:SOMFY: ".
- ":$sccMods:CUL_RFR::CUL_TCM97001:CUL_REDIRECT:";
- my $clientsHomeMatic = ":CUL_HM:HMS:CUL_IR:$sccMods:";
- my $clientsMAX = ":CUL_MAX:HMS:CUL_IR:$sccMods:";
- my $clientsWMBus = ":WMBUS:HMS:CUL_IR:$sccMods:";
- my $clientsKOPP_FC = ":KOPP_FC:HMS:CUL_IR:$sccMods:";
- my %matchListSlowRF = (
- "1:USF1000" => "^81..(04|0c)..0101a001a5ceaa00....",
- "2:BS" => "^81..(04|0c)..0101a001a5cf",
- "3:FS20" => "^81..(04|0c)..0101a001",
- "4:FHT" => "^81..(04|09|0d)..(0909a001|83098301|c409c401)..",
- "5:KS300" => "^810d04..4027a001",
- "6:CUL_WS" => "^K.....",
- "7:CUL_EM" => "^E0.................\$",
- "8:HMS" => "^810e04....(1|5|9).a001",
- "9:CUL_FHTTK" => "^T[A-F0-9]{8}",
- "A:CUL_RFR" => "^[0-9A-F]{4}U.",
- "B:CUL_HOERMANN"=> "^R..........",
- "C:ESA2000" => "^S................................\$",
- "D:CUL_IR" => "^I............",
- "E:CUL_TX" => "^TX[A-F0-9]{10}",
- "F:Revolt" => "^r......................\$",
- "G:IT" => "^i......",
- "H:STACKABLE_CC"=>"^\\*",
- "I:UNIRoll" => "^[0-9A-F]{5}(B|D|E)",
- "J:SOMFY" => "^Y[r|t|s]:?[A-F0-9]+",
- "K:CUL_TCM97001" => "^s[A-F0-9]+",
- "L:CUL_REDIRECT" => "^o+",
- "M:TSSTACKED"=>"^\\*",
- );
- my %matchListHomeMatic = (
- "1:CUL_HM" => "^A....................",
- "8:HMS" => "^810e04....(1|5|9).a001", # CUNO OneWire HMS Emulation
- "D:CUL_IR" => "^I............",
- "H:STACKABLE_CC"=>"^\\*",
- "M:TSSTACKED"=>"^\\*",
- );
- my %matchListMAX = (
- "1:CUL_MAX" => "^Z........................",
- "8:HMS" => "^810e04....(1|5|9).a001", # CUNO OneWire HMS Emulation
- "D:CUL_IR" => "^I............",
- "H:STACKABLE_CC"=>"^\\*",
- "M:TSSTACKED"=>"^\\*",
- );
- my %matchListWMBus = (
- "J:WMBUS" => "^b.*",
- "8:HMS" => "^810e04....(1|5|9).a001", # CUNO OneWire HMS Emulation
- "D:CUL_IR" => "^I............",
- "H:STACKABLE_CC"=>"^\\*",
- "M:TSSTACKED"=>"^\\*",
- );
- my %matchListKOPP_FC = (
- "1:Kopp_FC" => "^kr..................",
- "8:HMS" => "^810e04....(1|5|9).a001", # CUNO OneWire HMS Emulation
- "D:CUL_IR" => "^I............",
- "H:STACKABLE_CC"=>"^\\*",
- "M:TSSTACKED"=>"^\\*",
- );
- sub
- CUL_Initialize($)
- {
- my ($hash) = @_;
- require "$attr{global}{modpath}/FHEM/DevIo.pm";
- # Provider
- $hash->{ReadFn} = "CUL_Read";
- $hash->{WriteFn} = "CUL_Write";
- $hash->{ReadyFn} = "CUL_Ready";
- # Normal devices
- $hash->{DefFn} = "CUL_Define";
- $hash->{FingerprintFn} = "CUL_FingerprintFn";
- $hash->{UndefFn} = "CUL_Undef";
- $hash->{GetFn} = "CUL_Get";
- $hash->{SetFn} = "CUL_Set";
- $hash->{AttrFn} = "CUL_Attr";
- no warnings 'qw';
- my @attrList = qw(
- addvaltrigger
- connectCommand
- do_not_notify:1,0
- dummy:1,0
- hmId longids
- hmProtocolEvents:0_off,1_dump,2_dumpFull,3_dumpTrigger
- model:CUL,CUN,CUNO,SCC,nanoCUL
- rfmode:SlowRF,HomeMatic,MAX,WMBus_T,WMBus_S,KOPP_FC
- sendpool
- showtime:1,0
- );
- use warnings 'qw';
- $hash->{AttrList} = join(" ", @attrList)." ".$readingFnAttributes;
- $hash->{ShutdownFn} = "CUL_Shutdown";
- }
- sub
- CUL_FingerprintFn($$)
- {
- my ($name, $msg) = @_;
- # Store only the "relevant" part, as the CUL won't compute the checksum
- $msg = substr($msg, 8) if($msg =~ m/^81/ && length($msg) > 8);
- return ($name, $msg);
- }
- #####################################
- sub
- CUL_Define($$)
- {
- my ($hash, $def) = @_;
- my @a = split("[ \t][ \t]*", $def);
- if(@a < 4 || @a > 5) {
- my $msg = "wrong syntax: define <name> CUL {none | devicename[\@baudrate] ".
- "| devicename\@directio | hostname:port} <FHTID>";
- Log3 undef, 2, $msg;
- return $msg;
- }
- DevIo_CloseDev($hash);
- my $name = $a[0];
- my $dev = $a[2];
- return "FHTID must be H1H2, with H1 and H2 hex and both smaller than 64"
- if(uc($a[3]) !~ m/^[0-6][0-9A-F][0-6][0-9A-F]$/);
- if(uc($a[3]) =~ m/^([0-6][0-9A-F])/ && $1 ne "00") {
- my $x = $1;
- foreach my $d (keys %defs) {
- next if($d eq $name);
- if($defs{$d}{TYPE} =~ m/$culNameRe/) {
- if(uc($defs{$d}{FHTID}) =~ m/^$x/) {
- my $m = "$name: Cannot define multiple CULs with identical ".
- "first two digits ($x)";
- Log3 $name, 1, $m;
- return $m;
- }
- }
- }
- }
- $hash->{FHTID} = uc($a[3]);
- $hash->{initString} = "X21";
- $hash->{CMDS} = "";
- $hash->{Clients} = $clientsSlowRF;
- $hash->{MatchList} = \%matchListSlowRF;
- if($dev eq "none") {
- Log3 $name, 1, "$name device is none, commands will be echoed only";
- $attr{$name}{dummy} = 1;
- return undef;
- }
- $hash->{DeviceName} = $dev;
- my $ret = DevIo_OpenDev($hash, 0, "CUL_DoInit");
- return $ret;
- }
- #####################################
- sub
- CUL_Undef($$)
- {
- my ($hash, $arg) = @_;
- my $name = $hash->{NAME};
- foreach my $d (sort keys %defs) {
- if(defined($defs{$d}) &&
- defined($defs{$d}{IODev}) &&
- $defs{$d}{IODev} == $hash)
- {
- my $lev = ($reread_active ? 4 : 2);
- Log3 $name, $lev, "deleting port for $d";
- delete $defs{$d}{IODev};
- }
- }
- CUL_SimpleWrite($hash, "X00"); # Switch reception off, it may hang up the CUL
- DevIo_CloseDev($hash);
- return undef;
- }
- #####################################
- sub
- CUL_Shutdown($)
- {
- my ($hash) = @_;
- CUL_SimpleWrite($hash, "X00");
- return undef;
- }
- sub
- CUL_RemoveHMPair($)
- {
- my $hash = shift;
- delete($hash->{hmPair});
- }
- #####################################
- sub
- CUL_Reopen($)
- {
- my ($hash) = @_;
- DevIo_CloseDev($hash);
- DevIo_OpenDev($hash, 1, "CUL_DoInit");
- }
- #####################################
- sub
- CUL_Set($@)
- {
- my ($hash, @a) = @_;
- return "\"set CUL\" needs at least one parameter" if(@a < 2);
- return "Unknown argument $a[1], choose one of " . join(" ", sort keys %sets)
- if(!defined($sets{$a[1]}));
- my $name = shift @a;
- my $type = shift @a;
- my $arg = join("", @a);
- return "This command is not valid in the current rfmode"
- if($sets{$type} && $sets{$type} ne AttrVal($name, "rfmode", "SlowRF"));
- if($type eq "reopen") {
- CUL_Reopen($hash);
-
- } elsif($type eq "hmPairForSec") {
- return "Usage: set $name hmPairForSec <seconds_active>"
- if(!$arg || $arg !~ m/^\d+$/);
- $hash->{hmPair} = 1;
- InternalTimer(gettimeofday()+$arg, "CUL_RemoveHMPair", $hash, 1);
- } elsif($type eq "hmPairSerial") {
- return "Usage: set $name hmPairSerial <10-character-serialnumber>"
- if(!$arg || $arg !~ m/^.{10}$/);
- my $id = AttrVal($hash->{NAME}, "hmId", "F1".$hash->{FHTID});
- $hash->{HM_CMDNR} = $hash->{HM_CMDNR} ? ($hash->{HM_CMDNR}+1)%256 : 1;
- CUL_SimpleWrite($hash, sprintf("As15%02x8401%s000000010A%s",
- $hash->{HM_CMDNR}, $id, unpack('H*', $arg)));
- $hash->{hmPairSerial} = $arg;
- } elsif($type eq "freq") {
- my $f = $arg/26*65536;
- my $f2 = sprintf("%02x", $f / 65536);
- my $f1 = sprintf("%02x", int($f % 65536) / 256);
- my $f0 = sprintf("%02x", $f % 256);
- $arg = sprintf("%.3f", (hex($f2)*65536+hex($f1)*256+hex($f0))/65536*26);
- Log3 $name, 3, "Setting FREQ2..0 (0D,0E,0F) to $f2 $f1 $f0 = $arg MHz";
- CUL_SimpleWrite($hash, "W0F$f2");
- CUL_SimpleWrite($hash, "W10$f1");
- CUL_SimpleWrite($hash, "W11$f0");
- CUL_WriteInit($hash); # Will reprogram the CC1101
- } elsif($type eq "bWidth") {
- my ($err, $ob);
- if(!IsDummy($hash->{NAME})) {
- CUL_SimpleWrite($hash, "C10");
- ($err, $ob) = CUL_ReadAnswer($hash, $type, 0, "^C10 = .*");
- return "Can't get old MDMCFG4 value" if($err || $ob !~ m,/ (.*)\r,);
- $ob = $1 & 0x0f;
- }
- my ($bits, $bw) = (0,0);
- for (my $e = 0; $e < 4; $e++) {
- for (my $m = 0; $m < 4; $m++) {
- $bits = ($e<<6)+($m<<4);
- $bw = int(26000/(8 * (4+$m) * (1 << $e))); # KHz
- goto GOTBW if($arg >= $bw);
- }
- }
- GOTBW:
- $ob = sprintf("%02x", $ob+$bits);
- Log3 $name, 3, "Setting MDMCFG4 (10) to $ob = $bw KHz";
- CUL_SimpleWrite($hash, "W12$ob");
- CUL_WriteInit($hash);
- } elsif($type eq "rAmpl") {
- return "a numerical value between 24 and 42 is expected"
- if($arg !~ m/^\d+$/ || $arg < 24 || $arg > 42);
- my ($v, $w);
- for($v = 0; $v < @ampllist; $v++) {
- last if($ampllist[$v] > $arg);
- }
- $v = sprintf("%02d", $v-1);
- $w = $ampllist[$v];
- Log3 $name, 3, "Setting AGCCTRL2 (1B) to $v / $w dB";
- CUL_SimpleWrite($hash, "W1D$v");
- CUL_WriteInit($hash);
- } elsif($type eq "sens") {
- return "a numerical value between 4 and 16 is expected"
- if($arg !~ m/^\d+$/ || $arg < 4 || $arg > 16);
- my $w = int($arg/4)*4;
- my $v = sprintf("9%d",$arg/4-1);
- Log3 $name, 3, "Setting AGCCTRL0 (1D) to $v / $w dB";
- CUL_SimpleWrite($hash, "W1F$v");
- CUL_WriteInit($hash);
- } elsif( $type eq "ITClock" ) {
- my $clock = shift @a;
- $clock=250 if($clock eq "");
- return "argument $arg is not numeric" if($clock !~ /^\d+$/);
- Log3 $name, 3, "set $name $type $clock";
- $arg="ic$clock";
- CUL_SimpleWrite($hash, $arg);
- } else {
- return "Expecting a 0-padded hex number"
- if((length($arg)&1) == 1 && $type ne "raw");
- Log3 $name, 3, "set $name $type $arg";
- $arg = "l$arg" if($type eq "led");
- $arg = "x$arg" if($type eq "patable");
- CUL_SimpleWrite($hash, $arg);
- }
- return undef;
- }
- #####################################
- sub
- CUL_Get($@)
- {
- my ($hash, @a) = @_;
- my $type = $hash->{TYPE};
- return "\"get $type\" needs at least one parameter" if(@a < 2);
- if(!defined($gets{$a[1]})) {
- my @cList = map { $_ =~ m/^(file|raw)$/ ? $_ : "$_:noArg" } sort keys %gets;
- return "Unknown argument $a[1], choose one of " . join(" ", @cList);
- }
- my $arg = ($a[2] ? $a[2] : "");
- my ($msg, $err);
- my $name = $a[0];
- return "No $a[1] for dummies" if(IsDummy($name));
- if($a[1] eq "ccconf") {
- my %r = ( "0D"=>1,"0E"=>1,"0F"=>1,"10"=>1,"1B"=>1,"1D"=>1 );
- foreach my $a (sort keys %r) {
- CUL_SimpleWrite($hash, "C$a");
- ($err, $msg) = CUL_ReadAnswer($hash, "C$a", 0, "^C.* = .*");
- return $err if($err);
- my @answ = split(" ", $msg);
- $r{$a} = $answ[4];
- }
- $msg = sprintf("freq:%.3fMHz bWidth:%dKHz rAmpl:%ddB sens:%ddB",
- 26*(($r{"0D"}*256+$r{"0E"})*256+$r{"0F"})/65536, #Freq
- 26000/(8 * (4+(($r{"10"}>>4)&3)) * (1 << (($r{"10"}>>6)&3))), #Bw
- $ampllist[$r{"1B"}&7],
- 4+4*($r{"1D"}&3) #Sens
- );
- } else {
- CUL_SimpleWrite($hash, $gets{$a[1]}[0] . $arg);
- ($err, $msg) = CUL_ReadAnswer($hash, $a[1], 0, $gets{$a[1]}[1]);
- if(!defined($msg)) {
- DevIo_Disconnected($hash);
- $msg = "No answer";
- } elsif($a[1] eq "cmds") { # nice it up
- $msg =~ s/.*Use one of//g;
- } elsif($a[1] eq "uptime") { # decode it
- $msg =~ s/[\r\n]//g;
- $msg = hex($msg)/125;
- $msg = sprintf("%d %02d:%02d:%02d",
- $msg/86400, ($msg%86400)/3600, ($msg%3600)/60, $msg%60);
- } elsif($a[1] eq "credit10ms") {
- ($msg) = ($msg =~ /^.. *(\d*)[\r\n]*$/);
- }
- $msg =~ s/[\r\n]//g;
- }
- readingsSingleUpdate($hash, $a[1], $msg, 1);
- return "$a[0] $a[1] => $msg";
- }
- sub
- CUL_Clear($)
- {
- my $hash = shift;
- # Clear the pipe
- $hash->{RA_Timeout} = 0.1;
- for(;;) {
- my ($err, undef) = CUL_ReadAnswer($hash, "Clear", 0, "wontmatch");
- last if($err);
- }
- delete($hash->{RA_Timeout});
- $hash->{PARTIAL} = "";
- }
- #####################################
- sub
- CUL_DoInit($)
- {
- my $hash = shift;
- my $name = $hash->{NAME};
- my $err;
- my $msg = undef;
- CUL_Clear($hash);
- my ($ver, $try) = ("", 0);
- while($try++ < 3 && $ver !~ m/^V/) {
- CUL_SimpleWrite($hash, "V");
- ($err, $ver) = CUL_ReadAnswer($hash, "Version", 0, "^V");
- return "$name: $err" if($err && ($err !~ m/Timeout/ || $try == 3));
- $ver = "" if(!$ver);
- }
- if($ver !~ m/^V/) {
- $attr{$name}{dummy} = 1;
- $msg = "Not an CUL device, got for V: $ver";
- Log3 $name, 1, $msg;
- return $msg;
- }
- $ver =~ s/[\r\n]//g;
- $hash->{VERSION} = $ver;
- # Cmd-String feststellen
- my $cmds = CUL_Get($hash, $name, "cmds", 0);
- $cmds =~ s/$name cmds =>//g;
- $cmds =~ s/ //g;
- $hash->{CMDS} = $cmds;
- Log3 $name, 3, "$name: Possible commands: " . $hash->{CMDS};
- CUL_WriteInit($hash);
- # FHTID
- if(defined($hash->{FHTID})) {
- my $fhtid;
- CUL_SimpleWrite($hash, "T01");
- ($err, $fhtid) = CUL_ReadAnswer($hash, "FHTID", 0, undef);
- return "$name: $err" if($err);
- $fhtid =~ s/[\r\n]//g;
- Log3 $name, 5, "GOT CUL fhtid: $fhtid";
- if(!defined($fhtid) || $fhtid ne $hash->{FHTID}) {
- Log3 $name, 2, "Setting CUL fhtid from $fhtid to " . $hash->{FHTID};
- CUL_SimpleWrite($hash, "T01" . $hash->{FHTID});
- }
- }
- my $cc = AttrVal($name, "connectCommand", undef);
- CUL_SimpleWrite($hash, $cc) if($cc);
- readingsSingleUpdate($hash, "state", "Initialized", 1);
- # Reset the counter
- delete($hash->{XMIT_TIME});
- delete($hash->{NR_CMD_LAST_H});
- return undef;
- }
- #####################################
- # This is a direct read for commands like get
- # Anydata is used by read file to get the filesize
- sub
- CUL_ReadAnswer($$$$)
- {
- my ($hash, $arg, $anydata, $regexp) = @_;
- my $ohash = $hash;
- while($hash && $hash->{TYPE} !~ m/$culNameRe/) {
- $hash = $hash->{IODev};
- }
- return ("No FD", undef)
- if(!$hash || ($^O !~ /Win/ && !defined($hash->{FD})));
- my ($mculdata, $rin) = ("", '');
- my $buf;
- my $to = 3; # 3 seconds timeout
- $mculdata = $hash->{PARTIAL} if(defined($hash->{PARTIAL}));
- $to = $ohash->{RA_Timeout} if($ohash->{RA_Timeout}); # ...or less
- for(;;) {
- if($^O =~ m/Win/ && $hash->{USBDev}) {
- $hash->{USBDev}->read_const_time($to*1000); # set timeout (ms)
- # Read anstatt input sonst funzt read_const_time nicht.
- $buf = $hash->{USBDev}->read(999);
- return ("Timeout reading answer for get $arg", undef)
- if(length($buf) == 0);
- } else {
- return ("Device lost when reading answer for get $arg", undef)
- if(!$hash->{FD});
- vec($rin, $hash->{FD}, 1) = 1;
- my $nfound = select($rin, undef, undef, $to);
- if($nfound < 0) {
- next if ($! == EAGAIN() || $! == EINTR() || $! == 0);
- my $err = $!;
- DevIo_Disconnected($hash);
- return("CUL_ReadAnswer $arg: $err", undef);
- }
- return ("Timeout reading answer for get $arg", undef)
- if($nfound == 0);
- $buf = DevIo_SimpleRead($hash);
- return ("No data", undef) if(!defined($buf));
- }
- if(defined($buf)) {
- Log3 $ohash->{NAME}, 5, "CUL/RAW (ReadAnswer): $buf";
- $mculdata .= $buf;
- }
- # Dispatch data in the buffer before the proper answer.
- while(($mculdata =~ m/^([^\n]*\n)(.*)/s) || $anydata) {
- my $line = ($anydata ? $mculdata : $1);
- $mculdata = $2;
- $hash->{PARTIAL} = $mculdata; # for recursive calls
- (undef, $line) = CUL_prefix(0, $ohash, $line); # Delete prefix
- if($regexp && $line !~ m/$regexp/) {
- $line =~ s/[\n\r]+//g;
- CUL_Parse($ohash, $hash, $ohash->{NAME}, $line) if($init_done);
- $mculdata = $hash->{PARTIAL};
- } else {
- return (undef, $line);
- }
- }
- }
- }
- #####################################
- # Check if the 1% limit is reached and trigger notifies
- sub
- CUL_XmitLimitCheck($$$)
- {
- my ($hash,$fn,$now) = @_;
- if(!$hash->{XMIT_TIME}) {
- $hash->{XMIT_TIME}[0] = $now;
- $hash->{NR_CMD_LAST_H} = 1;
- return;
- }
- my $nowM1h = $now-3600;
- my @b = grep { $_ > $nowM1h } @{$hash->{XMIT_TIME}};
- # Maximum nr of transmissions per hour, but not for HM and MAX
- if(@b > 163 && $fn !~ m/^[AZ]/) {
- my $name = $hash->{NAME};
- Log3 $name, 2, "CUL TRANSMIT LIMIT EXCEEDED";
- DoTrigger($name, "TRANSMIT LIMIT EXCEEDED");
- } else {
- push(@b, $now);
- }
- $hash->{XMIT_TIME} = \@b;
- $hash->{NR_CMD_LAST_H} = int(@b);
- }
- sub
- CUL_XmitDlyHM($$$)
- {
- my ($hash,$fn,$now) = @_;
- my (undef,$mTy,undef,$id) = unpack 'A8A2A6A6',$fn if(length($fn)>19);
- if($id &&
- $modules{CUL_HM}{defptr}{$id} &&
- $modules{CUL_HM}{defptr}{$id}{helper}{io} &&
- $modules{CUL_HM}{defptr}{$id}{helper}{io}{nextSend}) {
- my $dDly = $modules{CUL_HM}{defptr}{$id}{helper}{io}{nextSend} - $now;
- #$dDly -= 0.04 if ($mTy eq "02");# while HM devices need a rest there are
- # still some devices that need faster
- # reactionfor ack.
- # Mode needs to be determined
- if ($dDly > 0.01){# wait less then 10 ms will not work
- $dDly = 0.1 if($dDly > 0.1);
- Log3 $hash->{NAME}, 5, "CUL $id dly:".int($dDly*1000)."ms";
- select(undef, undef, undef, $dDly);
- }
- }
- shift(@{$hash->{helper}{$id}{QUEUE}});
- InternalTimer($now+0.1, "CUL_XmitDlyHMTo", "$hash->{NAME}:$id", 1)
- if (scalar(@{$hash->{helper}{$id}{QUEUE}}));
- return 0;
- }
- sub
- CUL_XmitDlyHMTo($)
- { # waited long enough - next send for this ID
- my ($name,$id) = split(":",$_[0]);
- CUL_SendFromQueue($defs{$name}, ${$defs{$name}{helper}{$id}{QUEUE}}[0]);
- }
- #####################################
- # Translate data prepared for an FHZ to CUL syntax, so we can reuse
- # the FS20 and FHZ modules.
- sub
- CUL_WriteTranslate($$$)
- {
- my ($hash,$fn,$msg) = @_;
- ###################
- # Rewrite message from FHZ -> CUL
- if(length($fn) <= 1) { # CUL Native
- ;
- } elsif($fn eq "04" && substr($msg,0,6) eq "010101") { # FS20
- $fn = "F";
- AddDuplicate($hash->{NAME},
- "0101a001" . substr($msg, 6, 6) . "00" . substr($msg, 12));
- $msg = substr($msg,6);
- } elsif($fn eq "04" && substr($msg,0,6) eq "020183") { # FHT
- $fn = "T";
- $msg = substr($msg,6,4) . substr($msg,10);
- } elsif($fn eq "cmd") { # internal command
- if($msg eq "speed100") {
- $fn = "AR";
- } elsif($msg eq "speed10") {
- $fn = "Ar";
- } else { # by default rewrite init
- $fn = $hash->{initString};
- }
- $msg = "";
- } else {
- Log3 $hash, 2, "CUL cannot translate $fn $msg";
- return (undef, undef);
- }
- return ($fn, $msg);
- }
- #####################################
- sub
- CUL_Write($$$)
- {
- my ($hash,$fn,$msg) = @_;
- ($fn, $msg) = CUL_WriteTranslate($hash, $fn, $msg);
- return if(!defined($fn));
- my $name = $hash->{NAME};
- Log3 $name, 5, "$hash->{NAME} sending $fn$msg";
- my $bstring = "$fn$msg";
- if($fn eq "F" || # FS20 message
- $bstring =~ m/^u....F/ || # FS20 messages sent over an RFR
- ($fn eq "" && ($bstring =~ m/^A/ || $bstring =~ m/^Z/ ))) { # AskSin/BidCos/HomeMatic/MAX
- CUL_AddSendQueue($hash, $bstring);
- } else {
- CUL_SimpleWrite($hash, $bstring);
- }
- }
- sub
- CUL_SendFromQueue($$)
- {
- my ($hash, $bstring) = @_;
- my $name = $hash->{NAME};
- my $hm = ($bstring =~ m/^A/);
- my $to = ($hm ? 0.15 : 0.3);
- my $now = gettimeofday();
- if($bstring ne "") {
- my $sp = AttrVal($name, "sendpool", undef);
- if($sp) { # Is one of the CUL-fellows sending data?
- my @fellows = split(",", $sp);
- foreach my $f (@fellows) {
- if($f ne $name &&
- $defs{$f} &&
- $defs{$f}{QUEUE} &&
- $defs{$f}{QUEUE}->[0] ne ""){
- unshift(@{$hash->{QUEUE}}, "");
- InternalTimer($now+$to, "CUL_HandleWriteQueue", $hash, 1);
- return;
- }
- }
- }
- CUL_XmitLimitCheck($hash, $bstring, $now);
- if($hm) {
- CUL_SimpleWrite($hash, $bstring) if(!CUL_XmitDlyHM($hash,$bstring,$now));
- return;
- } else {
- CUL_SimpleWrite($hash, $bstring);
- }
- }
- ##############
- # Write the next buffer not earlier than 0.23 seconds
- # = 3* (12*0.8+1.2+1.0*5*9+0.8+10) = 226.8ms
- # else it will be sent too early by the CUL, resulting in a collision
- InternalTimer($now+$to, "CUL_HandleWriteQueue", $hash, 1);
- }
- sub
- CUL_AddSendQueue($$)
- {
- my ($hash, $bstring) = @_;
- my $qHash = $hash;
- if ($bstring =~ m/^A/){ # HM device
- my $id = substr($bstring,16,6);#get HMID destination
- $qHash = $hash->{helper}{$id};
- }
- if(!$qHash->{QUEUE} || 0 == scalar(@{$qHash->{QUEUE}})) {
- $qHash->{QUEUE} = [ $bstring ];
- CUL_SendFromQueue($hash, $bstring);
- } else {
- push(@{$qHash->{QUEUE}}, $bstring);
- }
- }
- #####################################
- sub
- CUL_HandleWriteQueue($)
- {
- my $hash = shift;
- my $arr = $hash->{QUEUE};
- if(defined($arr) && @{$arr} > 0) {
- shift(@{$arr});
- if(@{$arr} == 0) {
- delete($hash->{QUEUE});
- return;
- }
- my $bstring = $arr->[0];
- if($bstring eq "") {
- CUL_HandleWriteQueue($hash);
- } else {
- CUL_SendFromQueue($hash, $bstring);
- }
- }
- }
- #####################################
- # called from the global loop, when the select for hash->{FD} reports data
- sub
- CUL_Read($)
- {
- my ($hash) = @_;
- my $buf = DevIo_SimpleRead($hash);
- return "" if(!defined($buf));
- my $name = $hash->{NAME};
- my $culdata = $hash->{PARTIAL};
- Log3 $name, 5, "CUL/RAW: $culdata/$buf";
- $culdata .= $buf;
- while($culdata =~ m/\n/) {
- my $rmsg;
- ($rmsg,$culdata) = split("\n", $culdata, 2);
- $rmsg =~ s/\r//;
- $hash->{PARTIAL} = $culdata; # for recursive calls
- CUL_Parse($hash, $hash, $name, $rmsg) if($rmsg);
- $culdata = $hash->{PARTIAL};
- }
- $hash->{PARTIAL} = $culdata;
- }
- sub
- CUL_Parse($$$$@)
- {
- my ($hash, $iohash, $name, $rmsg, $initstr) = @_;
- if($rmsg =~ m/^\*/) { # STACKABLE_CC
- Dispatch($hash, $rmsg, undef);
- return;
- }
- if($rmsg =~ m/^V/) { # CUN* keepalive
- Log3 $name, 4, "CUL_Parse: $name $rmsg";
- return;
- }
- my $rssi;
- my $dmsg = $rmsg;
- my $dmsgLog = (AttrVal($name,"rfmode","") eq "HomeMatic")
- ? join(" ",(unpack'A1A2A2A4A6A6A*',$rmsg))
- :$dmsg;
- if($dmsg =~ m/^[AFTKEHRStZrib]([A-F0-9][A-F0-9])+$/) { # RSSI
- my $l = length($dmsg);
- $rssi = hex(substr($dmsg, $l-2, 2));
- $dmsg = substr($dmsg, 0, $l-2);
- $rssi = ($rssi>=128 ? (($rssi-256)/2-74) : ($rssi/2-74));
- Log3 $name, 4, "CUL_Parse: $name $dmsgLog $rssi";
- } else {
- Log3 $name, 4, "CUL_Parse: $name $dmsgLog";
- }
- ###########################################
- #Translate Message from CUL to FHZ
- next if(!$dmsg || length($dmsg) < 1); # Bogus messages
- if ($dmsg eq 'SMODE' || $dmsg eq 'TMODE') { # brs/brt returns SMODE/TMODE
- Log3 $name, 5, "CUL_Parse: switched to $dmsg";
- return;
- }
- if($dmsg =~ m/^[0-9A-F]{4}U./) { # RF_ROUTER
- Dispatch($hash, $dmsg, undef);
- return;
- }
- my $fn = substr($dmsg,0,1);
- my $len = length($dmsg);
- if($fn eq "F" && $len >= 9) { # Reformat for 10_FS20.pm
- CUL_AddSendQueue($iohash, ""); # Delay immediate replies
- $dmsg = sprintf("81%02x04xx0101a001%s00%s",
- $len/2+7, substr($dmsg,1,6), substr($dmsg,7));
- $dmsg = lc($dmsg);
- } elsif($fn eq "T") {
- if ($len >= 11) { # Reformat for 11_FHT.pm
- $dmsg = sprintf("81%02x04xx0909a001%s00%s",
- $len/2+7, substr($dmsg,1,6), substr($dmsg,7));
- $dmsg = lc($dmsg);
- } else {
- ; # => 09_CUL_FHTTK.pm
- }
- } elsif($fn eq "H" && $len >= 13) { # Reformat for 12_HMS.pm
- my $type = hex(substr($dmsg,6,1));
- my $stat = $type > 1 ? hex(substr($dmsg,7,2)) : hex(substr($dmsg,5,2));
- my $prf = $type > 1 ? "02" : "05";
- my $bat = $type > 1 ? hex(substr($dmsg,5,1))+1 : 1;
- my $HA = substr($dmsg,1,4);
- my $values = $type > 1 ? "000000" : substr($dmsg,7);
- $dmsg = sprintf("81%02x04xx%s%x%xa001%s0000%02x%s",
- $len/2+8, # Packet-Length
- $prf, $bat, $type,
- $HA, # House-Code
- $stat,
- $values); # Values
- $dmsg = lc($dmsg);
- } elsif($fn eq "K" && $len >= 5) {
- if($len == 15) { # Reformat for 13_KS300.pm
- my @a = split("", $dmsg);
- $dmsg = sprintf("81%02x04xx4027a001", $len/2+6);
- for(my $i = 1; $i < 14; $i+=2) { # Swap nibbles.
- $dmsg .= $a[$i+1] . $a[$i];
- }
- $dmsg = lc($dmsg);
- }
- # Other K... Messages ar sent to CUL_WS
- } elsif($fn eq "r" && $len >= 23) { # Revolt
- $dmsg = lc($dmsg);
- } elsif($fn eq "i" && $len >= 7) { # IT
- $dmsg = lc($dmsg);
- } elsif($fn eq "Y" && $len >= 3) { # SOMFY RTS
- ;
- } elsif($fn eq "S" && $len >= 33) { # CUL_ESA / ESA2000 / Native
- ;
- } elsif($fn eq "E" && $len >= 11) { # CUL_EM / Native
- ;
- } elsif($fn eq "R" && $len >= 11) { # CUL_HOERMANN / Native
- ;
- } elsif($fn eq "I" && $len >= 12) { # IR-CUL/CUN/CUNO
- ;
- } elsif($fn eq "A" && $len >= 20) { # AskSin/BidCos/HomeMatic
- my $src = substr($dmsg,9,6);
- if($modules{CUL_HM}{defptr}{$src}){
- $modules{CUL_HM}{defptr}{$src}{helper}{io}{nextSend} =
- gettimeofday() + 0.100;
- }
- $dmsg .= "::$rssi:$name" if(defined($rssi));
- } elsif($fn eq "Z" && $len >= 21) { # Moritz/Max
- ;
- } elsif($fn eq "b" && $len >= 24) { # Wireless M-Bus
- $dmsg .= "::$rssi" if (defined($rssi));
- } elsif($fn eq "t" && $len >= 5) { # TX3
- $dmsg = "TX".substr($dmsg,1); # t.* is occupied by FHTTK
- } elsif($fn eq "s" && $len >= 5) { # CUL_TCM97001
- ;
- } elsif($fn eq "o" && $len >= 5) { # CUL_REDIRECT
- ;
- } elsif($fn eq "k" && $len >= 20) { # KOPP_FC
- ;
- } else {
- DoTrigger($name, "UNKNOWNCODE $dmsg");
- Log3 $name, 2, "$name: unknown message $dmsg";
- return;
- }
- $hash->{"${name}_MSGCNT"}++;
- $hash->{"${name}_TIME"} = TimeNow();
- # showtime attribute
- readingsSingleUpdate($hash, "state", $hash->{READINGS}{state}{VAL}, 0);
- $hash->{RAWMSG} = $rmsg;
- my %addvals = (RAWMSG => $dmsg);
- if(defined($rssi)) {
- $hash->{RSSI} = $rssi;
- $addvals{RSSI} = $rssi;
- }
- Dispatch($hash, $dmsg, \%addvals);
- }
- #####################################
- sub
- CUL_Ready($)
- {
- my ($hash) = @_;
- return DevIo_OpenDev($hash, 1, "CUL_DoInit")
- if($hash->{STATE} eq "disconnected");
- # This is relevant for windows/USB only
- my $po = $hash->{USBDev};
- my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags);
- if($po) {
- ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status;
- }
- return ($InBytes && $InBytes>0);
- }
- ########################
- # Needed for STACKABLE_CC
- sub
- CUL_WriteInit($)
- {
- my ($hash) = @_;
- foreach my $is (split("\n", $hash->{initString})) {
- CUL_SimpleWrite($hash, $is);
- }
- }
- sub
- CUL_SimpleWrite(@)
- {
- my ($hash, $msg, $nonl) = @_;
- return if(!$hash);
- ($hash, $msg) = CUL_prefix(1, $hash, $msg);
- my $name = $hash->{NAME};
- if (AttrVal($name,"rfmode","") eq "HomeMatic"){
- Log3 $name, 4, "CUL_send: $name".join(" ",unpack('A2A2A2A4A6A6A*',$msg));
- }
- else{
- Log3 $name, 5, "SW: $msg";
- }
- $msg .= "\n" unless($nonl);
- $hash->{USBDev}->write($msg) if($hash->{USBDev});
- syswrite($hash->{TCPDev}, $msg) if($hash->{TCPDev});
- syswrite($hash->{DIODev}, $msg) if($hash->{DIODev});
- # Some linux installations are broken with 0.001, T01 returns no answer
- select(undef, undef, undef, 0.01);
- }
- sub
- CUL_Attr(@)
- {
- my ($cmd,$name,$aName,$aVal) = @_;
- my $hash = $defs{$name};
- if($aName eq "rfmode") {
- $aVal = "SlowRF" if(!$aVal ||
- ($aVal ne "HomeMatic"
- && $aVal ne "MAX"
- && $aVal ne "WMBus_T"
- && $aVal ne "WMBus_S"
- && $aVal ne "KOPP_FC"));
- my $msg = $hash->{NAME} . ": Mode $aVal not supported";
- if($aVal eq "HomeMatic") {
- return if($hash->{initString} =~ m/Ar/);
- if($hash->{CMDS} =~ m/A/ || IsDummy($hash->{NAME}) || !$hash->{FD}) {
- $hash->{Clients} = $clientsHomeMatic;
- $hash->{MatchList} = \%matchListHomeMatic;
- CUL_SimpleWrite($hash, "Zx") if ($hash->{CMDS} =~ m/Z/); # reset Moritz
- $hash->{initString} = "X21\nAr"; # X21 is needed for RSSI reporting
- CUL_WriteInit($hash);
- } else {
- Log3 $name, 2, $msg;
- return $msg;
- }
- } elsif($aVal eq "MAX") {
- return if($hash->{initString} =~ m/Zr/);
- if($hash->{CMDS} =~ m/Z/ || IsDummy($hash->{NAME}) || !$hash->{FD}) {
- $hash->{Clients} = $clientsMAX;
- $hash->{MatchList} = \%matchListMAX;
- CUL_SimpleWrite($hash, "Ax") if ($hash->{CMDS} =~ m/A/); # reset AskSin
- $hash->{initString} = "X21\nZr"; # X21 is needed for RSSI reporting
- CUL_WriteInit($hash);
- } else {
- Log3 $name, 2, $msg;
- return $msg;
- }
- } elsif($aVal eq "WMBus_S") {
- return if($hash->{initString} =~ m/brs/);
- if($hash->{CMDS} =~ m/b/ || IsDummy($hash->{NAME}) || !$hash->{FD}) {
- $hash->{Clients} = $clientsWMBus;
- $hash->{MatchList} = \%matchListWMBus;
- $hash->{initString} = "X21\nbrs"; # Use S-Mode
- CUL_WriteInit($hash);
-
- } else {
- Log3 $name, 2, $msg;
- return $msg;
- }
- } elsif($aVal eq "WMBus_T") {
- return if($hash->{initString} =~ m/brt/);
- if($hash->{CMDS} =~ m/b/ || IsDummy($hash->{NAME}) || !$hash->{FD}) {
- $hash->{Clients} = $clientsWMBus;
- $hash->{MatchList} = \%matchListWMBus;
- $hash->{initString} = "X21\nbrt"; # Use T-Mode
- CUL_WriteInit($hash);
-
- } else {
- Log3 $name, 2, $msg;
- return $msg;
- }
- } elsif($aVal eq "KOPP_FC") {
- if($hash->{CMDS} =~ m/k/ || IsDummy($hash->{NAME}) || !$hash->{FD}) {
- $hash->{Clients} = $clientsKOPP_FC;
- $hash->{MatchList} = \%matchListKOPP_FC;
- $hash->{initString} = "krS"; # krS: start Kopp receive Mode
- CUL_WriteInit($hash);
- } else {
- Log3 $name, 2, $msg;
- return $msg;
- }
- } else {
- return if($hash->{initString} eq "X21");
- $hash->{Clients} = $clientsSlowRF;
- $hash->{MatchList} = \%matchListSlowRF;
- $hash->{initString} = "X21";
- CUL_SimpleWrite($hash, "Ax") if ($hash->{CMDS} =~ m/A/); # reset AskSin
- CUL_SimpleWrite($hash, "Zx") if ($hash->{CMDS} =~ m/Z/); # reset Moritz
- CUL_SimpleWrite($hash, "brx") if ($hash->{CMDS} =~ m/b/); # reset WMBus
- CUL_WriteInit($hash);
- }
- Log3 $name, 2, "Switched $name rfmode to $aVal";
- delete $hash->{".clientArray"};
- } elsif($aName eq "hmId"){
- if($cmd eq "set") {
- return "wrong syntax: hmId must be 6-digit-hex-code (3 byte)"
- if($aVal !~ m/^[A-F0-9]{6}$/i);
- }
- } elsif($aName eq "connectCommand"){
- CUL_SimpleWrite($hash, $aVal) if($cmd eq "set");
- }
- return undef;
- }
- sub
- CUL_prefix($$$)
- {
- my ($isadd, $hash, $msg) = @_;
- while($hash && $hash->{TYPE} !~ m/$culNameRe/) {
- $msg = CallFn($hash->{NAME}, $isadd ? "AddPrefix":"DelPrefix", $hash, $msg);
- $hash = $hash->{IODev};
- last if(!$hash);
- }
- return ($hash, $msg);
- }
- 1;
- =pod
- =item summary connect devices with the culfw Firmware, e.g. Busware CUL
- =item summary_DE Anbindung von Geraeten mit dem culfw Firmware, z.Bsp. Busware CUL
- =begin html
- <a name="CUL"></a>
- <h3>CUL</h3>
- <ul>
- <table>
- <tr><td>
- The CUL/CUN(O) is a family of RF devices sold by <a
- href="http://www.busware.de">busware.de</a>.
- With the opensource firmware
- <a href="http://culfw.de/culfw.html">culfw</a> they are capable
- to receive and send different 433/868 MHz protocols (FS20/FHT/S300/EM/HMS/MAX!).
- It is even possible to use these devices as range extenders/routers, see the
- <a href="#CUL_RFR">CUL_RFR</a> module for details.
- <br> <br>
- Some protocols (FS20, FHT and KS300) are converted by this module so that
- the same logical device can be used, irrespective if the radio telegram is
- received by a CUL or an FHZ device.<br>
- Other protocols (S300/EM) need their
- own modules. E.g. S300 devices are processed by the CUL_WS module if the
- signals are received by the CUL, similarly EMWZ/EMGZ/EMEM is handled by the
- CUL_EM module.<br><br>
- It is possible to attach more than one device in order to get better
- reception, FHEM will filter out duplicate messages.<br><br>
- Note: This module may require the <code>Device::SerialPort</code> or
- <code>Win32::SerialPort</code> module if you attach the device via USB
- and the OS sets strange default parameters for serial devices.<br><br>
- </td><td>
- <img src="ccc.jpg"/>
- </td></tr>
- </table>
- <a name="CULdefine"></a>
- <b>Define</b>
- <ul>
- <code>define <name> CUL <device> <FHTID></code> <br>
- <br>
- USB-connected devices (CUL/CUN):<br><ul>
- <device> specifies the serial port to communicate with the CUL.
- The name of the serial-device depends on your distribution, under
- linux the cdc_acm kernel module is responsible, and usually a
- /dev/ttyACM0 device will be created. If your distribution does not have a
- cdc_acm module, you can force usbserial to handle the CUL by the
- following command:
- <ul><code>
- modprobe usbserial vendor=0x03eb product=0x204b
- </code></ul>
- In this case the device is most probably /dev/ttyUSB0.<br><br>
- You can also specify a baudrate if the device name contains the @
- character, e.g.: /dev/ttyACM0@38400<br><br>
- If the baudrate is "directio" (e.g.: /dev/ttyACM0@directio), then the
- perl module <code>Device::SerialPort</code> is not needed, and FHEM
- opens the device with simple file io. This might work if the operating
- system uses sane defaults for the serial parameters, e.g. some Linux
- distributions and OSX.<br><br>
- </ul>
- Network-connected devices (CUN(O)):<br><ul>
- <device> specifies the host:port of the device, e.g.
- 192.168.0.244:2323
- </ul>
- <br>
- If the device is called none, then no device will be opened, so you
- can experiment without hardware attached.<br>
- The FHTID is a 4 digit hex number, and it is used when the CUL talks to
- FHT devices or when CUL requests data. Set it to 0000 to avoid answering
- any FHT80b request by the CUL.
- </ul>
- <br>
- <a name="CULset"></a>
- <b>Set </b>
- <ul>
- <li>reopen<br>
- Reopens the connection to the device and reinitializes it.
- </li><br>
- <li>raw<br>
- Issue a CUL firmware command. See the <a
- href="http://culfw.de/commandref.html">this</a> document
- for details on CUL commands.
- </li><br>
- <li>freq / bWidth / rAmpl / sens<br>
- <a href="#rfmode">SlowRF</a> mode only.<br>
- Set the CUL frequency / bandwidth / receiver-amplitude / sensitivity<br>
- Use it with care, it may destroy your hardware and it even may be
- illegal to do so. Note: The parameters used for RFR transmission are
- not affected.<br>
- <ul>
- <li>freq sets both the reception and transmission frequency. Note:
- Although the CC1101 can be set to frequencies between 315 and 915
- MHz, the antenna interface and the antenna of the CUL is tuned for
- exactly one frequency. Default is 868.3 MHz (or 433 MHz)</li>
- <li>bWidth can be set to values between 58 kHz and 812 kHz. Large values
- are susceptible to interference, but make possible to receive
- inaccurately calibrated transmitters. It affects tranmission too.
- Default is 325 kHz.</li>
- <li>rAmpl is receiver amplification, with values between 24 and 42 dB.
- Bigger values allow reception of weak signals. Default is 42.
- </li>
- <li>sens is the decision boundary between the on and off values, and it
- is 4, 8, 12 or 16 dB. Smaller values allow reception of less clear
- signals. Default is 4 dB.</li>
- </ul>
- </li><br>
- <a name="hmPairForSec"></a>
- <li>hmPairForSec<br>
- <a href="#rfmode">HomeMatic</a> mode only.<br>
- Set the CUL in Pairing-Mode for the given seconds. Any HM device set into
- pairing mode in this time will be paired with FHEM.
- </li><br>
- <a name="hmPairSerial"></a>
- <li>hmPairSerial<br>
- <a href="#rfmode">HomeMatic</a> mode only.<br>
- Try to pair with the given device. The argument is a 10 character
- string, usually starting with letters and ending with digits, printed on
- the backside of the device. It is not necessary to put the given device
- in learning mode if it is a receiver.
- </li><br>
- <a name="hmPairForSec"></a>
- <li>led<br>
- Set the CUL led off (00), on (01) or blinking (02).
- </li><br>
- <li>ITClock</br>
- Set the IT clock for Intertechno V1 protocol. Default 250.
- </li><br>
- </ul>
- <a name="CULget"></a>
- <b>Get</b>
- <ul>
- <li>version<br>
- returns the CUL firmware version
- </li><br>
- <li>uptime<br>
- returns the CUL uptime (time since CUL reset)
- </li><br>
- <li>raw<br>
- Issues a CUL firmware command, and waits for one line of data returned by
- the CUL. See the CUL firmware README document for details on CUL
- commands.
- </li><br>
- <li>fhtbuf<br>
- CUL has a message buffer for the FHT. If the buffer is full, then newly
- issued commands will be dropped, and an "EOB" message is issued to the
- FHEM log.
- <code>fhtbuf</code> returns the free memory in this buffer (in hex),
- an empty buffer in the CUL V2 is 74 bytes, in CUL V3/CUN(O) 200 Bytes.
- A message occupies 3 + 2x(number of FHT commands) bytes,
- this is the second reason why sending multiple FHT commands with one
- <a href="#set">set</a> is a good idea. The first reason is, that
- these FHT commands are sent at once to the FHT.
- </li> <br>
- <li>ccconf<br>
- Read some CUL radio-chip (cc1101) registers (frequency, bandwidth, etc.),
- and display them in human readable form.
- </li><br>
- <li>cmds<br>
- Depending on the firmware installed, CULs have a different set of
- possible commands. Please refer to the README of the firmware of your
- CUL to interpret the response of this command. See also the raw command.
- </li><br>
- <li>credit10ms<br>
- One may send for a duration of credit10ms*10 ms before the send limit
- is reached and a LOVF is generated.
- </li><br>
- </ul>
- <a name="CULattr"></a>
- <b>Attributes</b>
- <ul>
- <li><a name="addvaltrigger">addvaltrigger</a><br>
- Create triggers for additional device values. Right now these are RSSI
- and RAWMSG for the CUL family and RAWMSG for the FHZ.
- </li><br>
- <li><a name="connectCommand">connectCommand</a><br>
- raw culfw command sent to the CUL after a (re-)connect of the USB device,
- and sending the usual initialization needed for the configured rfmode.
- </li>
- <li><a href="#do_not_notify">do_not_notify</a></li>
- <li><a href="#attrdummy">dummy</a></li>
- <li><a name="hmId">hmId</a><br>
- Set the HomeMatic ID of this device. If this attribute is absent, the
- ID will be F1<FHTID>. Note 1: After setting or changing this
- attribute you have to relearn all your HomeMatic devices. Note 2: The
- value <b>must</b> be a 6 digit hex number, and 000000 is not valid. FHEM
- won't complain if it is not correct, but the communication won't work.
- </li><br>
- <li><a name="hmProtocolEvents">hmProtocolEvents</a><br>
- Generate events for HomeMatic protocol messages. These are normally
- used for debugging, by activating "inform timer" in a telnet session,
- or looking at the Event Monitor window in the FHEMWEB frontend.<br>
- Example:
- <ul>
- <code>
- 2012-05-17 09:44:22.515 CUL CULHM RCV L:0B N:81 CMD:A258 SRC:......
- DST:...... 0000 (TYPE=88,WAKEMEUP,BIDI,RPTEN)
- </code>
- </ul>
- </li><br>
-
- <li><a name="longids">longids</a><br>
- Comma separated list of device-types for CUL that should be handled
- using long IDs. This additional ID allows it to differentiate some
- weather sensors, if they are sending on the same channel.
- Therefore a random generated id is added. If you choose to use longids,
- then you'll have to define a different device after battery change.
- Default is not to use long IDs.<br>
- Modules which are using this functionality are for e.g. :
- 14_Hideki, 41_OREGON, 14_CUL_TCM97001, 14_SD_WS07.<br>
- Examples:<br>
- <ul><code>
- # Do not use any long IDs for any devices (this is default):<br>
- attr cul longids 0<br>
- # Use long IDs for all devices:<br>
- attr cul longids 1<br>
- # Use longids for SD_WS07 devices.<br>
- # Will generate devices names like SD_WS07_TH_3 for channel 3.<br>
- attr cul longids SD_WS07
- </code></ul>
- </li><br>
- <li><a href="#model">model</a> (CUL,CUN,etc)</li>
- <li><a name="sendpool">sendpool</a><br>
- If using more than one CUL for covering a large area, sending
- different events by the different CUL's might disturb each other. This
- phenomenon is also known as the Palm-Beach-Resort effect.
- Putting them in a common sendpool will serialize sending the events.
- E.g. if you have three CUN's, you have to specify following
- attributes:<br>
- <code>attr CUN1 sendpool CUN1,CUN2,CUN3<br>
- attr CUN2 sendpool CUN1,CUN2,CUN3<br>
- attr CUN3 sendpool CUN1,CUN2,CUN3</code><br>
- </li><br>
- <li><a name="rfmode">rfmode</a><br>
- Configure the RF Transceiver of the CUL (the CC1101). Available
- arguments are:
- <ul>
- <li>SlowRF<br>
- To communicate with FS20/FHT/HMS/EM1010/S300/Hoermann devices @1 kHz
- datarate. This is the default.</li>
- <li>HomeMatic<br>
- To communicate with HomeMatic type of devices @10 kHz datarate.</li>
- <li>MAX<br>
- To communicate with MAX! type of devices @10 kHz datarate.</li>
- <li>WMBus_S</li>
- <li>WMBus_T<br>
- To communicate with Wireless M-Bus devices like water, gas or
- electrical meters. Wireless M-Bus uses two different communication
- modes, S-Mode and T-Mode. While in this mode, no reception of other
- protocols like SlowRF or HomeMatic is possible. See also the WMBUS
- FHEM Module.
- </li>
- </ul>
- </li><br>
- <li><a href="#showtime">showtime</a></li>
-
- <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
- </ul>
- <br>
- </ul>
- =end html
- =begin html_DE
- <a name="CUL"></a>
- <h3>CUL</h3>
- <ul>
- <table>
- <tr><td>
- Der CUL/CUN(O) ist eine Familie von Funkempfängern, die von der Firma
- <a href="http://www.busware.de">Busware</a> verkauft wird.
- Mit der OpenSource Firmware
- <a href="http://culfw.de/culfw.html">culfw</a> können sie verschiedene
- 868 MHz Funkprotokolle empfangen bzw. senden (FS20/FHT/S300/EM/HMS/MAX!).
- Man kann diese Geräte auch zur Reichweitenverlängerung, siehe
- <a href="#CUL_RFR">CUL_RFR</a> einsetzen.
- <br> <br>
- Einige Protokolle (FS20, FHT und KS300) werden von diesem Modul in das FHZ
- Format konvertiert, daher kann dasselbe logische Gerät verwendet werden,
- egal ob das Funktelegramm von einem CUL oder einem FHZ Gerät empfangen
- wird.<br>
- Andere Protokolle (S300/EM) benötigen ihre eigenen Module. S300
- Geräte werden vom Modul CUL_WS verarbeitet, wenn das Signal von einem
- CUL empfangen wurde, ähnliches gilt für EMWZ/EMGZ/EMEM: diese
- werden vom CUL_EM Modul verarbeitet.<br><br>
- Es ist möglich mehr als ein Gerät zu verwenden, um einen besseren
- Empfang zu erhalten, FHEM filtert doppelte Funktelegramme aus.<br><br>
- Bemerkung: Dieses Modul benötigt unter Umständen das
- <code>Device::SerialPort</code> bzw. <code>Win32::SerialPort</code> Modul,
- wenn Sie das Gerät über USB anschließen und das
- Betriebssystem unübliche Parameter für serielle Schnittstellen
- setzt.<br><br>
- </td><td>
- <img src="ccc.jpg"/>
- </td></tr>
- </table>
- <a name="CULdefine"></a>
- <b>Define</b>
- <ul>
- <code>define <name> CUL <device> <FHTID></code> <br>
- <br>
- Geräte, die an USB angeschlossen sind (CUL/CUN):<br>
- <ul>
- <device> gibt die serielle Schnittstelle an, mit der der CUL
- kommuniziert. Der Name der seriellen Schnittstelle hängt von der
- gewählten Distribution und USB-Treiber ab, unter Linux ist dies das
- Kernel Modul cdc_acm und üblicherweise wird die Schnittstelle
- /dev/ttyACM0 genannt. Wenn die Linux Distribution über kein Kernel
- Modul cdc_acm verfügt, dann kann die Schnittstelle über
- usbserial mit dem folgenden Befehl erzeugt werden:
- <ul><code>
- modprobe usbserial vendor=0x03eb product=0x204b
- </code></ul>
- In diesem Fall ist diese Schnittstelle dann wahrscheinlich
- /dev/ttyUSB0.<br><br>
- Wenn der Name der Schnittstelle ein @ enthält, kann nachfolgend die
- verwendete Baudrate angegeben werden, z.B.: /dev/ttyACM0@38400.<br><br>
- Wenn die Baudrate mit "directio" angegeben wird (z.B.:
- /dev/ttyACM0@directio), wird das Perl Modul
- <code>Device::SerialPort</code> nicht benötigt und FHEM öffnet
- die Schnittstelle mit einfachem Dateizugriff. Dies sollte dann
- funktionieren, wenn das Betriebssystem vernünftige Standardwerte
- für die serielle Schnittstelle verwendet, wie z.B. einige Linux
- Distributionen oder OSX.<br><br>
- </ul>
- Geräte, die mit dem Netzwerk verbunden sind (CUN(O)):<br>
- <ul>
- <device> gibt die Hostadresse:Port des Gerätes an, z.B.
- 192.168.0.244:2323
- </ul>
- <br>
- Wenn das Gerät mit none bezeichnet wird, wird keine Schnittstelle
- geöffnet und man kann ohne angeschlossene Hardware
- experimentieren.<br>
- Die FHTID ist eine 4-stellige hexadezimale Zahl und wird verwendet, wenn
- der CUL FHT Telegramme sendet bzw. Daten anfragt. Diese sollte als 0000
- gewählt werden, wenn man FHT80b Anfragen durch den CUL vermeiden will.
- </ul>
- <br>
- <a name="CULset"></a>
- <b>Set </b>
- <ul>
- <li>reopen<br>
- Öffnet die Verbindung zum Gerät neu und initialisiert es.
- </li><br>
- <li>raw<br>
- Sendet einen CUL Firmware Befehl. Siehe auch
- <a href="http://culfw.de/commandref.html">hier</a> für
- nähere Erläuterungen der CUL Befehle.
- </li><br>
- <li>freq / bWidth / rAmpl / sens<br>
- Nur in der Betriebsart <a href="#rfmode">SlowRF</a>.<br> Bestimmt die
- CUL Frequenz / Bandbreite / Empfänger Amplitude /
- Empfindlichkeit<br>
- Bitte mit Vorsicht verwenden, da es die verwendete Hardware
- zerstören kann bzw. es zu illegalen Funkzuständen kommen
- kann. <br> Bemerkung: Die Parameter für die RFR Übermittlung
- werden hierdurch nicht beeinflußt.<br>
- <ul>
- <li>freq bestimmt sowohl die Empfangs- als auch die Sendefrequenz.<br>
- Bemerkung: Auch wenn der CC1101 zwischen den Frequenzen 315 und 915
- MHz eingestellt werden kann, ist die Antennenanbindung bzw. die
- Antenne des CUL exakt auf eine Frequenz eingestellt. Standard ist
- 868.3 MHz (bzw. 433 MHz).</li>
- <li>bWidth kann zwischen 58 kHz und 812 kHz variiert werden.
- Große Werte sind empfindlicher gegen Interferencen, aber
- machen es möglich, nicht genau kalbrierte Signale zu
- empfangen. Die Einstellung beeinflusst ebenso die Übertragung.
- Standardwert ist 325 kHz.</li>
- <li>rAmpl ist die Verstärkung des Empfängers mit Werten
- zwischen 24 and 42 dB. Größere Werte erlauben den
- Empfang von schwachen Signalen. Standardwert ist 42.</li>
- <li>sens ist die Entscheidungsgrenze zwischen "on" und "off"
- Zuständen und kann 4, 8, 12 oder 16 dB sein. Kleinere Werte
- erlauben den Empfang von undeutlicheren Signalen. Standard ist 4
- dB.</li>
- </ul>
- </li><br>
- <a name="hmPairForSec"></a>
- <li>hmPairForSec<br>
- Nur in der Betriebsart <a href="#rfmode">HomeMatic</a>.<br> Versetzt den
- CUL für die angegebene Zeit in Sekunden in den Anlern-Modus. Jedes
- HM Gerät, das sich im Anlern-Modus befindet, wird an FHEM
- angelernt. </li><br>
- <a name="hmPairSerial"></a>
- <li>hmPairSerial<br>
- Nur in der Betriebsart <a href="#rfmode">HomeMatic</a>.<br>
- Versucht, das angegebene Gerät anzulernen (zu "pairen"). Der
- Parameter ist eine 10-stellige Zeichenfolge, die normalerweise mit
- Buchstaben beginnt und mit Ziffern endet; diese sind auf der
- Rückseite der Geräte aufgedruckt. Wenn das Gerät ein
- Empfänger ist, ist es nicht notwendig, das angegebene Gerät in
- den Anlern-Modus zu versetzen. </li><br>
- <a name="hmPairForSec"></a>
- <li>led<br>
- Schaltet die LED des CUL: aus (00), an (01) oder blinkend (02).
- </li><br>
- <li>ITClock</br>
- Setzt die IT clock fü Intertechno V1 Protokoll. Default 250.
- </li><br>
- </ul>
- <a name="CULget"></a>
- <b>Get</b>
- <ul>
- <li>version<br>
- gibt die Version der CUL Firmware zurück
- </li><br>
- <li>uptime<br>
- gibt die Betriebszeit des CULs zurück (Zeit seit dem letzten Reset
- des CULs) </li><br>
- <li>raw<br>
- Sendet einen CUL Firmware Befehl und wartet auf eine Rückgabe des
- CULs. Siehe auch README der Firmware für nähere
- Erläuterungen zu den CUL Befehlen. </li><br>
- <li>fhtbuf<br>
- Der CUL hat einen Puffer für Nachrichten für FHT. Wenn der
- Puffer voll ist, werden neu empfangene Telegramme ignoriert und eine
- "EOB" Meldung wird in die FHEM Logdatei geschrieben.
- <code>fhtbuf</code> gibt den freien Speicher dieses Puffers (in hex)
- zurück, ein leerer Puffer im CUL V2 hat 74 Byte, im CUL V3/CUN(O)
- hat 200 Byte. Eine Telegramm benötigt 3 + 2x(Anzahl der FHT
- Befehle) Byte, dies ist ein Grund, warum man mehrere FHT Befehle mit
- einem <a href="#set">set</a> senden sollte. Ein weiterer Grund ist,
- dass diese FHT Befehle in einem "Paket" zum FHT Gerät gesendet werden.
- </li> <br>
- <li>ccconf<br>
- Liest einige CUL Register des CC1101 (Sende- und Empfängerchips)
- aus (Frequenz, Bandbreite, etc.) und stellt diese in lesbarer Form dar.
- </li><br>
- <li>cmds<br>
- In abhägigkeit der installierten Firmware hat der CUL/CUN(O)
- unterschiedliche Befehlssätze. Nähere Informationen über
- die Befehle bzw. deren Interpretation siehe README Datei der
- verwendeten CUL Firmware. Siehe auch Anmerkungen beim raw Befehl.
- </li><br>
- <li>credit10ms<br>
- Der Funkraum darf für eine Dauer von credit10ms*10 ms belegt
- werden, bevor die gesetzliche 1% Grenze erreicht ist und eine
- LOVF Meldung ausgegeben wird. </li><br> </ul>
- <a name="CULattr"></a>
- <b>Attribute</b>
- <ul>
- <li><a name="addvaltrigger">addvaltrigger</a><br>
- Generiert Trigger für zusätzliche Werte. Momentan sind dies
- RSSI und RAWMSG für die CUL Familie und RAWMSG für FHZ.
- </li><br>
- <li><a name="connectCommand">connectCommand</a><br>
- culfw Befehl, was nach dem Verbindungsaufbau mit dem USB-Gerät, nach
- Senden der zum Initialisieren der konfigurierten rfmode benötigten
- Befehle gesendet wird.
- </li>
- <li><a href="#do_not_notify">do_not_notify</a></li>
- <li><a href="#attrdummy">dummy</a></li>
- <li><a name="hmId">hmId</a><br>
- Setzt die HomeMatic ID des Gerätes. Wenn dieses Attribut fehlt,
- wird die ID zu F1<FHTID> gesetzt. Bemerkung 1: Nach dem Setzen
- bzw. Verändern dieses Attributes müssen alle HomeMatic
- Geräte neu angelernt werden. Bemerkung 2: Der Wert <b>muss</b>
- eine 6-stellige Hexadezimalzahl sein, 000000 ist ungültig. FHEM
- überprüft nicht, ob die ID korrekt ist, im Zweifelsfall
- funktioniert die Kommunikation nicht. </li><br>
- <li><a name="hmProtocolEvents">hmProtocolEvents</a><br>
- Generiert Ereignisse für HomeMatic Telegramme. Diese werden
- normalerweise für die Fehlersuche verwendet, z.B. durch Aktivieren
- von <code>inform timer</code> in einer telnet Sitzung bzw. im
- <code>Event Monitor</code> Fenster im FHEMWEB Frontend.<br>
- Beispiel:
- <ul>
- <code>
- 2012-05-17 09:44:22.515 CUL CULHM RCV L:0B N:81 CMD:A258 SRC:......
- DST:...... 0000 (TYPE=88,WAKEMEUP,BIDI,RPTEN)
- </code>
- </ul>
- </li><br>
- <li><a name="longids">longids</a><br>
- Durch Kommata getrennte Liste von Device-Typen für Empfang von
- langen IDs mit den CUL. Diese zusätzliche ID erlaubt es
- Wettersensoren, welche auf dem gleichen Kanal senden zu unterscheiden.
- Hierzu wird eine zufällig generierte ID hinzugefügt. Wenn Sie
- longids verwenden, dann wird in den meisten Fällen nach einem
- Batteriewechsel ein neuer Sensor angelegt.
- Standardmäßig werden keine langen IDs verwendet.<br>
- Folgende Module verwenden diese Funktionalität:
- 14_Hideki, 41_OREGON, 14_CUL_TCM97001, 14_SD_WS07.<br>
- Beispiele:
- <ul><code>
- # Keine langen IDs verwenden (Default Einstellung):<br>
- attr cul longids 0<br>
- # Immer lange IDs verwenden:<br>
- attr cul longids 1<br>
- # Verwende lange IDs für SD_WS07 Devices.<br>
- # Device Namen sehen z.B. so aus: SD_WS07_TH_3 for channel 3.<br>
- attr cul longids SD_WS07
- </code></ul>
- </li><br>
-
- <li><a href="#model">model</a> (CUL,CUN)</li><br>
- <li><a name="rfmode">rfmode</a><br>
- Konfiguriert den RF Transceiver des CULs (CC1101). Verfügbare
- Argumente sind:
- <ul>
- <li>SlowRF<br>
- Für die Kommunikation mit FS20/FHT/HMS/EM1010/S300/Hoermann
- Geräten @1 kHz Datenrate (Standardeinstellung).</li>
- <li>HomeMatic<br>
- Für die Kommunikation mit HomeMatic Geräten @10 kHz
- Datenrate.</li>
- <li>MAX<br>
- Für die Kommunikation mit MAX! Geräten @10 kHz
- Datenrate.</li>
- <li>WMBus_S</li>
- <li>WMBus_T<br>
- Für die Kommunikation mit Wireless M-Bus Geräten wie
- Wasser-, Gas- oder Elektrozählern. Wireless M-Bus verwendet
- zwei unterschiedliche Kommunikationsarten, S-Mode und T-Mode. In
- diesem Modus ist der Empfang von anderen Protokollen wie SlowRF
- oder HomeMatic nicht möglich.</li>
- </ul>
- </li><br>
- <li><a name="sendpool">sendpool</a><br>
- Wenn mehr als ein CUL verwendet wird, um einen größeren
- Bereich abzudecken, können diese sich gegenseitig
- beeinflussen. Dieses Phänomen wird auch Palm-Beach-Resort Effekt
- genannt. Wenn man diese zu einen gemeinsamen Sende"pool"
- zusammenschließt, wird das Senden der einzelnen Telegramme
- seriell (d.h. hintereinander) durchgeführt.
- Wenn z.B. drei CUN's zur
- Verfügung stehen, werden folgende Attribute gesetzt:<br>
- <code>attr CUN1 sendpool CUN1,CUN2,CUN3<br>
- attr CUN2 sendpool CUN1,CUN2,CUN3<br>
- attr CUN3 sendpool CUN1,CUN2,CUN3</code><br>
- </li><br>
- <li><a href="#showtime">showtime</a></li>
- <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
- </ul>
- <br>
- </ul>
- =end html_DE
- =cut
|