47_OBIS.pm 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942
  1. ###############################################
  2. #
  3. # 47_OBIS.pm
  4. #
  5. # Thanks to matzefizi for letting me merge this with 70_SMLUSB.pm and for testing
  6. # Tanks to immi for testing and supporting help and tips
  7. #
  8. # $Id: 47_OBIS.pm 16167 2018-02-13 20:36:00Z Icinger $
  9. # Removed: PERL WARNING: Hexadecimal number > 0xffffffff non-portable at
  10. # Added: attr ExtChannels -> set History-Readings
  11. package main;
  12. use strict;
  13. use warnings;
  14. use Time::HiRes qw(gettimeofday usleep);
  15. use Scalar::Util qw(looks_like_number);
  16. use POSIX qw{strftime};
  17. no warnings 'portable'; # Support for 64-bit ints required
  18. #use Math::BigInt ':constant';
  19. my %OBIS_channels = ( "21" =>"power_L1",
  20. "41" =>"power_L2",
  21. "61" =>"power_L3",
  22. "12" =>"voltage_avg",
  23. "32" =>"voltage_L1",
  24. "52" =>"voltage_L2",
  25. "72" =>"voltage_L3",
  26. "11" =>"current_sum",
  27. "31" =>"current_L1",
  28. "51" =>"current_L2",
  29. "71" =>"current_L3",
  30. "1.8" =>"total_consumption",
  31. "2.8" =>"total_feed",
  32. "2" =>"feed_L1",
  33. "4" =>"feed_L2",
  34. "6" =>"feed_L3",
  35. "1" =>"power",
  36. "15" =>"power",
  37. "16" =>"power",
  38. "24" =>"Gas",
  39. );
  40. my %OBIS_codes = ( "Serial" => qr{^0-0:96.1.255(?:.\d+)?\((.*?)\).*},
  41. "Serial" => qr{^(?:1-0:)?0\.0\.[1-9]+(?:.\d+)?\((.*?)\).*},
  42. "Owner" => qr{^1.0.0.0.0(?:.\d+)?\((.*?)\).*}x,
  43. "Status" => qr{^1.0.96.5.5(?:.\d+)?\((.*?)\).*}x,
  44. "Powerdrops" => qr{^0.0.96.7.\d(?:.\d+)?\((.*?)\).*},
  45. "Time_param" => qr{^0.0.96.2.1(?:.\d+)?\((.*?)\).*},
  46. "Time_current" => qr{^0.0.1.0.0(?:.\d+)?\((.*?)\).*},
  47. "Channel_sum" => qr{^(?:1.0.)?(\d+).1.7(?:.0|.255)?(?:\(.*?\))?\((<|>)?([-+]?\d+\.?\d*)\*?(.*)\).*},
  48. "Channels" => qr{^(?:\d.0.)?(\d+).7\.\d+(?:.0|.255)?(?:\(.*?\))?\((<|>)?([-+]?\d+\.?\d*)\*?(.*)\).*},
  49. "Channels2" => qr{^(?:0.1.)?(\d+).2\.\d+(?:.0|.255)?(?:\(.*?\))?\((<|>)?(-?\d+\.?\d*)\*?(.*)\).*},
  50. "Counter" => qr{^(?:1.\d.)?(\d).(8)\.(\d).(\d+)?(?:\(.*?\))?\((<|>)?(-?\d+\.?\d*)\*?(.*)\).*}, #^(?:1.\d.)?(\d).(8)\.(\d)(?:.\d+)?(?:\(.*?\))?\((<|>)?(-?\d+\.?\d*)\*?(.*)\).*
  51. "ManufID" => qr{^129-129:199\.130\.3(?:.\d+)?\((.*?)\).*},
  52. "PublicKey" => qr{^129-129:199\.130\.5(?:.\d+)?\((.*?)\).*},
  53. );
  54. my %SML_specialities = ("TIME" => [qr{0.0.96.2.1
  55. |0.0.1.0.0 }x, sub{return strftime("%d-%m-%Y %H:%M:%S", localtime(unpack("i", pack("I", hex(@_)))))}],
  56. "HEX2" => [qr{1-0:0\.0\.[0-9] }x, sub{my $a=shift;
  57. if ( $a =~ /^[0-9a-fA-F]+$/ ) {$a=~s/(..)/$1-/g;$a=~s/-$//};
  58. return $a;}],
  59. "HEX4" => [qr{1.0.96.5.5
  60. |0.0.96.240.\d+
  61. |129.129.199.130.5}x, sub{my $a=shift;
  62. if ( $a =~ /^[0-9a-fA-F]+$/ ) {$a=~s/(....)/$1-/g;$a=~s/-$//};
  63. return $a;}],
  64. "INFO" => [qr{1-0:0\.0\.[0-9]
  65. |129.129.199.130.3}x, ""],
  66. );
  67. #####################################
  68. sub OBIS_Initialize($)
  69. {
  70. my ($hash) = @_;
  71. require "$attr{global}{modpath}/FHEM/DevIo.pm";
  72. $hash->{Match} = "^\/(?s:.*)\!\$";
  73. $hash->{ReadFn} = "OBIS_Read";
  74. $hash->{ReadyFn} = "OBIS_Ready";
  75. $hash->{DefFn} = "OBIS_Define";
  76. $hash->{ParseFn} = "OBIS_Parse";
  77. $hash->{GetFn} = "OBIS_Get";
  78. $hash->{UndefFn} = "OBIS_Undef";
  79. $hash->{AttrFn} = "OBIS_Attr";
  80. $hash->{AttrList}= "do_not_notify:1,0 interval offset_feed offset_energy IODev channels directions alignTime pollingMode:on,off extChannels:on,off unitReadings:on,off ignoreUnknown:on,off valueBracket:first,second,both createPreValues:on,off ".
  81. $readingFnAttributes;
  82. }
  83. #1B1B1B1B010101017605002605426200620072630101760107FFFFFFFFFFFF05000CAC6C0B0A01454D4800005A561C726201640DF29B620163676F0076050026054362006200726307017707FFFFFFFFFFFF0B0A01454D4800005A561C070100620AFFFF726201640DF29B7577070100603201010101010104454D480177070100600100FF010101010B0A01454D4800005A561C0177070100010800FF641C0104726201640DF29B621E52FF6404AB760177070100020800FF01726201640DF29B621E52FF6324910177070100100700FF0101621B52005301360101016373F100760500260544620062007263020171016326A3000000001B1B1B1B1A03CDC9
  84. #1B1B1B1B010101017605002605456200620072630101760107FFFFFFFFFFFF05000CAC6D0B0A01454D4800005A561C726201640DF29C62016343D30076050026054662006200726307017707FFFFFFFFFFFF0B0A01454D4800005A561C070100620AFFFF726201640DF29C7577070100603201010101010104454D480177070100600100FF010101010B0A01454D4800005A561C0177070100010800FF641C0104726201640DF29C621E52FF6404AB770177070100020800FF01726201640DF29C621E52FF6324910177070100100700FF0101621B520053012C0101016319F6007605002605476200620072630201710163955D000000001B1B1B1B1A0310B4
  85. #####################################
  86. sub OBIS_Define($$)
  87. {
  88. my ($hash, $def) = @_;
  89. my @a = split("[ \t][ \t]*", $def);
  90. return 'wrong syntax: define <name> OBIS devicename@baudrate[,databits,parity,stopbits]|none [MeterType]'
  91. if(@a < 3);
  92. DevIo_CloseDev($hash);
  93. RemoveInternalTimer($hash);
  94. my $name = $a[0];
  95. my $dev = $a[2];
  96. my $type = $a[3]//"Unknown";
  97. $hash->{DeviceName} = $dev;
  98. $hash->{MeterType}=$type if (defined($type));
  99. my $device_name = "OBIS_".$name;
  100. $modules{OBIS}{defptr}{$device_name} = $hash;
  101. # If device="none", prepeare for an external IO-Module
  102. if($dev=~/none|ext/) {
  103. if (@a == 4){
  104. my $device_name = "OBIS.".$a[4];
  105. $hash->{CODE} = $a[4];
  106. $modules{OBIS}{defptr}{$device_name} = $hash;
  107. }
  108. AssignIoPort($hash);
  109. Log3 ($hash,1, "OBIS ($name) - OBIS device is none, commands will be echoed only");
  110. return undef;
  111. }
  112. my $baudrate;
  113. my $devi;
  114. ($devi, $baudrate) = split("@", $dev);
  115. $hash->{helper}{SPEED}="0";
  116. if (defined($baudrate)) { ## added for ser2net connection
  117. if($baudrate =~ m/(\d+)(,([78])(,([NEO])(,([012]))?)?)?/) {
  118. $baudrate = $1 if(defined($1));
  119. }
  120. my %bd=("300"=>"0","600"=>"1","1200"=>"2","2400"=>"3","4800"=>"4","9600"=>"5","18200"=>"6","36400"=>"7","57600"=>"8","115200"=>"9");
  121. $hash->{helper}{SPEED}=$bd{$baudrate};
  122. $hash->{helper}{SPEED2}=$bd{$a[4]//$baudrate};
  123. }
  124. else {$baudrate=9600; $hash->{helper}{SPEED}="5";}
  125. my %devs= (
  126. # Name, Init-String, interval, 2ndInit
  127. "none" => ["", -1, ""],
  128. "Unknown" => ["", -1, ""],
  129. "SML" => ["", -1, ""],
  130. "Ext" => ["", -1, ""],
  131. "Standard" => ["", -1, ""],
  132. "VSM102" => ["/?!".chr(13).chr(10), 600, chr(6)."0".$hash->{helper}{SPEED}."0".chr(13).chr(10)],
  133. "E110" => ["/?!".chr(13).chr(10), 600, chr(6)."0".$hash->{helper}{SPEED}."0".chr(13).chr(10)],
  134. "E350USB" => ["/?!".chr(13).chr(10), 600, chr(6)."0".$hash->{helper}{SPEED}."0".chr(13).chr(10)],
  135. "AS1440" => ["/2!".chr(13).chr(10), 600, chr(6)."0".$hash->{helper}{SPEED}."0".chr(13).chr(10)]
  136. );
  137. if (!$devs{$type}) {return 'unknown meterType. Must be one of <nothing>, SML, Standard, VSM102, E110'};
  138. $devs{$type}[1] = $hash->{helper}{DEVICES}[1] // $devs{$type}[1];
  139. $hash->{helper}{DEVICES} =$devs{$type};
  140. $hash->{helper}{TRIGGERTIME}=gettimeofday();
  141. # if( !$init_done ) {
  142. # $attr{$name}{"event-on-change-reading"} = ".*";
  143. # }
  144. my $t=OBIS_adjustAlign($hash,AttrVal($name,"alignTime",undef),$hash->{helper}{DEVICES}[1]);
  145. Log3 ($hash,5,"OBIS ($name) - Internal timer set to ".FmtDateTime($t)) if ($hash->{helper}{DEVICES}[1]>0);
  146. InternalTimer($t, "GetUpdate", $hash, 0) if ($hash->{helper}{DEVICES}[1]>0);
  147. $hash->{helper}{EoM}=-1;
  148. Log3 $hash,5,"OBIS ($name) - Opening device...";
  149. return DevIo_OpenDev($hash, 0, "OBIS_Init");
  150. }
  151. sub OBIS_Get($@)
  152. {
  153. my ($hash, @a) = @_;
  154. my $name = shift @a;
  155. my $opt = shift @a;
  156. if ($opt eq "update") {
  157. GetUpdate($hash);
  158. } else
  159. {return "Unknown argument $opt, choose one of update";}
  160. }
  161. sub OBIS_Set($@)
  162. {
  163. my ( $hash, @a ) = @_;
  164. my $name = shift @a;
  165. my $opt = shift @a;
  166. my $value = join("", @a);
  167. my $teststr="";
  168. my %bd=("300"=>"0","600"=>"1","1200"=>"2","2400"=>"3","4800"=>"4","9600"=>"5","18200"=>"6","36400"=>"7","57600"=>"8","115200"=>"9");
  169. if ($opt eq "setSpeed") {
  170. if ($bd{$value} ne $hash->{helper}{SPEED}) {
  171. $hash->{helper}{BUFFER}="";
  172. $hash->{helper}{SPEED}=$bd{$value};
  173. print "old Helper: $hash->{helper}{DEVICES}[2] \r\n";
  174. $hash->{helper}{DEVICES}[2]=$hash->{helper}{DEVICES}[2] eq "" ? "" : chr(6)."0".$hash->{helper}{SPEED}."0".chr(13).chr(10);
  175. print "new Helper: $hash->{helper}{DEVICES}[2] \r\n";
  176. $hash->{helper}{SpeedChange}=$value;
  177. DevIo_SimpleWrite($hash,$hash->{helper}{DEVICES}[0],undef) ;
  178. print "Wrote $hash->{helper}{DEVICES}[0]\r\n";
  179. }
  180. }
  181. return;
  182. }
  183. # Update-Routine
  184. sub GetUpdate($)
  185. {
  186. my ($hash) = @_;
  187. my $name = $hash->{NAME};
  188. my $type= $hash->{MeterType};
  189. RemoveInternalTimer($hash);
  190. $hash->{helper}{EoM}=-1;
  191. if ($hash->{helper}{DEVICES}[1] eq "") {return undef;}
  192. if( $init_done ) {
  193. DevIo_SimpleWrite($hash,$hash->{helper}{DEVICES}[0],undef) ;
  194. Log3 $hash,4,"Wrote $hash->{helper}{DEVICES}[0]";
  195. }
  196. my $t=OBIS_adjustAlign($hash,AttrVal($name,"alignTime",undef),$hash->{helper}{DEVICES}[1]);
  197. Log3 ($hash,5,"OBIS ($name) - Internal timer set to ".FmtDateTime($t)) if ($hash->{helper}{DEVICES}[1]>0);
  198. InternalTimer($t, "GetUpdate", $hash, 1) if ($hash->{helper}{DEVICES}[1]>0);
  199. }
  200. sub OBIS_Init($)
  201. {
  202. Log 3,"Init done";
  203. return undef;
  204. }
  205. #####################################
  206. sub OBIS_Undef($$)
  207. {
  208. my ($hash, $arg) = @_;
  209. RemoveInternalTimer($hash);
  210. DevIo_CloseDev($hash) if $hash->{DeviceName} ne "none";
  211. return undef;
  212. }
  213. #####################################
  214. sub OBIS_Read($)
  215. {
  216. my ($hash) = @_;
  217. if( $init_done ) {
  218. my $name = $hash->{NAME};
  219. my $buf = DevIo_SimpleRead($hash);
  220. my $b=$buf;
  221. $b =~ s/(.)/sprintf("%X",ord($1))/eg;
  222. if ( !defined($hash->{helper}{SpeedChange}) || ($hash->{helper}{SpeedChange} eq ""))
  223. {
  224. OBIS_Parse($hash,$buf) if ($hash->{helper}{EoM}!=1);
  225. } else
  226. {
  227. # if ($hash->{helper}{SpeedChange2} eq "")
  228. # {
  229. # Log3 $hash,4,"Part 1";
  230. ## $hash->{helper}{SPEED}=$bd{$value};
  231. # DevIo_SimpleWrite($hash,$hash->{helper}{DEVICES}[2],undef) ;
  232. # Log3 $hash,4,"Writing ".$hash->{helper}{DEVICES}[2];
  233. # $hash->{helper}{SpeedChange2}="1";
  234. # } elsif ($hash->{helper}{SpeedChange2} eq "1")
  235. # {
  236. # if ($buf ne hex(15)) {
  237. # Log3 $hash,4,"Part 2";
  238. # my $sp=$hash->{helper}{SPEED};
  239. # my $d=$hash->{DeviceName};
  240. # my $repl=$sp;
  241. # Log3 $hash,4,"Old Dev: $d";
  242. # $d=~/(.*@)(\d*)(.*)/;
  243. # my $d2=$1.$hash->{helper}{SpeedChange}.$3;
  244. # # $d=~s/(.*@)(\d*)(.*)/$repl$2/ee;
  245. #
  246. # Log3 $hash,4, "Replaced dev: $d2";
  247. # RemoveInternalTimer($hash);
  248. # DevIo_CloseDev($hash) if $hash->{DeviceName} ne "none";
  249. # $hash->{DeviceName} = $d2;
  250. # $hash->{helper}{EoM}=-1;
  251. # Log3 $hash,5,"OBIS ($name) - Opening device...";
  252. # my $t=OBIS_adjustAlign($hash,AttrVal($name,"alignTime",undef),$hash->{helper}{DEVICES}[1]);
  253. # Log3 ($hash,5,"OBIS ($name) - Internal timer set to ".FmtDateTime($t)) if ($hash->{helper}{DEVICES}[1]>0);
  254. # InternalTimer($t, "GetUpdate", $hash, 0) if ($hash->{helper}{DEVICES}[1]>0);
  255. # DevIo_OpenDev($hash, 1, "OBIS_Init");
  256. # } else
  257. # {
  258. # Log3 $hash,4,"Recieved NAK from Meter";
  259. # }
  260. # $hash->{helper}{SpeedChange2}="";
  261. # $hash->{helper}{SpeedChange}="";
  262. # Log3 $hash,4, "Cleared helper\r\n";
  263. # }
  264. }
  265. }
  266. return(undef);
  267. }
  268. sub OBIS_trySMLdecode($$)
  269. {
  270. my ( $hash, $remainingSML ) = @_;
  271. my $t=$remainingSML;
  272. if ($remainingSML=~m/SML\((.*)\)/g) {$remainingSML=$1};
  273. if($remainingSML!~/[\x00-\x09|\x10-\x1F]/g) {return $remainingSML} else {$hash->{MeterType}="SML"};
  274. $remainingSML=uc(unpack('H*',$remainingSML));
  275. $hash->{MeterType}="SML";
  276. my $newMsg="";
  277. while ($remainingSML=~/(1B1B1B1B010101.*?1B1B1B1B1A[0-9A-F]{6})/mip) {
  278. my $msg=$1;
  279. Log3 $hash,5,"SML-Parse $1";
  280. $remainingSML=${^POSTMATCH};
  281. if (OBIS_CRC16($hash,pack('H*',$msg)) == 1) {
  282. $remainingSML=""; #reset possible further messages if actual CRC ok; if someone misses some messages, we remove it.
  283. my $OBISmsg="";
  284. my $initstr="/";
  285. my $OBISid=$msg=~m/7701([0-9A-F]*?)01/g;
  286. Log3 $hash,5,"OBIS: Full message-> $msg";
  287. (undef,undef,$OBISid,undef)=OBIS_decodeTL($1);
  288. while ($msg =~ m/(7707)([0-9A-F]*)/g) {
  289. my @list=$&=~/(7707)([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]*)/g;
  290. Log3 $hash, 5,"OBIS: Telegram=$msg";
  291. if (!@list) {Log3 $hash,3,"OBIS - Empty datagram: .$msg"};
  292. my $line=hex($list[1])."-".hex($list[2]).":".hex($list[3]).".".hex($list[4]).".".hex($list[5])."*255(";
  293. if ($line eq '255-255:255.255.255*255(') {
  294. $list[7]=~/(7707.*)/;
  295. $msg=$1;
  296. } else
  297. {
  298. my ($status,$statusL,$statusT,$valTime,$valTimeL,$valTimeT,$unit,$unitL,$unitT,$scaler,$scalerL,$scalerT,$data,$dataL,$dataT,$other);
  299. ($statusL,$statusT,$status,$other)=OBIS_decodeTL($list[7]);
  300. ($valTimeL,$valTimeT,$valTime,$other)=OBIS_decodeTL($other);
  301. ($unitL,$unitT,$unit,$other)=OBIS_decodeTL($other);
  302. ($scalerL,$scalerT,$scaler,$other)=OBIS_decodeTL($other);
  303. ($dataL,$dataT,$data,$msg)=OBIS_decodeTL($other);
  304. my $line2="";
  305. if ($dataT ==0 ) {
  306. $line2=$data;
  307. if($line=~$SML_specialities{"HEX4"}[0]) {
  308. # $line2=$SML_specialities{"HEX4"}[1]->($data)
  309. } elsif($line=~$SML_specialities{"HEX2"}[0]) {
  310. # $line2=$SML_specialities{"HEX2"}[1]->($data)
  311. } else {
  312. $data=~s/([A-F0-9]{2})/chr(hex($1))/eg;
  313. $data=~s/[^!-~\s]//g;
  314. $line2="$data";
  315. }
  316. # Type signed (=80) and unsigned (=96) Integer
  317. } elsif ($dataT & 0b01010000|$dataT & 0b01100000) {
  318. $unit= $unit eq "1E" ? "Wh" :
  319. $unit eq "1B" ? "W" :
  320. $unit eq "21" ? "A" :
  321. $unit eq "23" ? "V" :
  322. $unit eq "2C" ? "Hz" :
  323. $unit eq "01" ? "" :
  324. $unit eq "1D" ? "varh" :
  325. $unit eq "" ? "" : "var";
  326. $scaler=$scaler ne "" ? 10**unpack("c", pack("C", hex($scaler))) : 1;
  327. if ($scaler==0) {$scaler=1}; # just to make sure
  328. $line2.="<" if ($status=~/[aA]2$/);
  329. $line2.=">" if ($status=~/82$/);
  330. my $val=0;
  331. # signed Values
  332. my $tmp="";
  333. if ($dataT & 0b00010000) {
  334. if ($data =~ /^[89a-f]/i) {$val = hex($data) - hex(sprintf ("FF" x $dataL)) -1;}
  335. else {$val = hex($data)} #positive value
  336. }
  337. if ($dataT & 0b00100000 || $val>=0) {
  338. $val=hex($data);
  339. }
  340. $line2.=($val*$scaler).($unit eq "" ? "" : "*$unit"); # if($dataT ==96);
  341. } elsif ($dataT & 0b01000000) { # Type Boolean - no Idea, where this is used
  342. $line2=OBIS_hex2int($data); # 0=false, everything else is true
  343. } elsif ($dataT & 0b01110000) { # Type List of.... - Time is sometimes delivered as structure
  344. # my @a_Length;
  345. # my @a_Type;
  346. # my @a_Data;
  347. # for (my $b=0;$b<$dataL;$b++) {
  348. # my ($l_length,$l_type,$l_data);
  349. # }
  350. }
  351. $initstr.="$line2\\" if ($line=~$SML_specialities{"INFO"}[0]);
  352. $newMsg.=$line.$line2.")\r\n";
  353. ###### TypeLength-Test ends here
  354. }
  355. }
  356. $initstr=~s/\\$//;
  357. $newMsg=$initstr.chr(13).chr(10).$newMsg;
  358. $newMsg.="!".chr(13).chr(10);
  359. Log3 $hash,4,"MSG IS: \r\n$newMsg";
  360. } else {
  361. $hash->{CRC_Errors}+=1;
  362. }
  363. }
  364. return ($newMsg,pack('H*',$remainingSML));
  365. }
  366. sub OBIS_Parse($$)
  367. {
  368. my ($hash, $buf) = @_;
  369. my $buf2=uc(unpack('H*',$buf));
  370. if($hash->{MeterType}!~/SML|Unknown/ && $buf2=~m/7701([0-9A-F]*?)01/g) {
  371. Log 3,"OBIS_Ext called";
  372. my (undef,undef,$OBISid,undef)=OBIS_decodeTL($1);
  373. my $device_name = "OBIS.".$OBISid;
  374. Log3 $hash,5,"New Devicename: $device_name";
  375. my $def = $modules{OBIS}{defptr}{"$device_name"};
  376. if(!$def) {
  377. Log3 $hash, 3, "OBIS: Unknown device $device_name, please define it";
  378. return "UNDEFINED $device_name OBIS none Ext $OBISid";
  379. }
  380. }
  381. $hash->{helper}{BUFFER} .= $buf;
  382. if (length($hash->{helper}{BUFFER}) >10000) { #longer than 3 messages, this is a traffic jam
  383. $hash->{helper}{BUFFER} =substr( $hash->{helper}{BUFFER} , -10000);
  384. }
  385. my %dir=("<"=>"out",">"=>"in");
  386. my $buffer=$hash->{helper}{BUFFER};
  387. my $remainingSML;
  388. ($buffer,$remainingSML) = OBIS_trySMLdecode($hash,$buffer) if ($hash->{MeterType}=~/SML|Ext|Unknown/);
  389. my $type= $hash->{MeterType};
  390. my $name = $hash->{NAME};
  391. $buf='/'.$buf;
  392. $buf =~ /!((?!\/).*)$/gmsi;
  393. $buf=$1;
  394. if(index($buffer,chr(13).chr(10)) ne -1){
  395. readingsBeginUpdate($hash);
  396. while(index($buffer,chr(13).chr(10)) ne -1)
  397. {
  398. my $rmsg="";
  399. $rmsg = substr($buffer, 0, index($buffer,chr(13).chr(10)));
  400. Log3 $hash,5,"OBIS ($name) - Msg-Parse: $rmsg";
  401. my $channel=" ";
  402. if($rmsg=~/\/.*|^((?:\d{1,3}-\d{1,3}:)?(?:\d{1,3}|[CF]).\d{1,3}(?:.\d{1,3})?(?:\*\d{1,3})?)(?:\(.*?\))?\(.*?\)|!/) { # old regex: \/.*|\d-\d{1,3}:\d{1,3}.\d{1,3}.\d{1,3}\*\d{1,3}\(.*?\)|!
  403. if (length $1) {
  404. $channel=$1;
  405. # $channel=~s/[\:\-*]/\./;
  406. $channel=~s/:/\./;
  407. $channel=~s/-/\./;
  408. $channel=~s/\*/\./;
  409. # Log 3,"Channel would be: $channel";
  410. }
  411. if ($hash->{MeterType} eq "Unknown") {$hash->{MeterType}="Standard"}
  412. # if($rmsg=~/^([23456789]+)-.*/) {
  413. # Log3 $hash,3,"OBIS ($name) - Unknown OBIS-Device, please report: $rmsg".chr(13).chr(10)."Please report to User icinger at forum.fhem.de";
  414. # }
  415. # End of Message
  416. if ($rmsg=~/^!.*/) {
  417. $hash->{helper}{EoM}+=1 if ($hash->{helper}{DEVICES}[1]>0);
  418. }
  419. #Version
  420. elsif ($rmsg=~ /.*\/(.*)/) {
  421. DevIo_SimpleWrite($hash,$hash->{helper}{DEVICES}[2],undef) if (!$hash->{helper}{DEVICES}[2] eq "");
  422. if (ReadingsVal($name,"Version","") ne $1) {readingsBulkUpdate($hash, "Version" ,$1); }
  423. $hash->{helper}{EoM}=0;
  424. }
  425. elsif ($hash->{helper}{EoM}!=1) {
  426. my @patterns=values %OBIS_codes;
  427. if (!$rmsg~~@patterns) {
  428. Log3 $hash,3,"OBIS ($name) - Unknown Message: $rmsg".chr(13).chr(10)."Please report to User icinger at forum.fhem.de"
  429. } else {
  430. my $found=0;
  431. if (!($hash->{helper}{Channels}{$channel} //$hash->{helper}{Channels}{$1})) {
  432. for my $code (keys %OBIS_codes) {
  433. if ($rmsg =~ $OBIS_codes{$code}) {
  434. Log3 $hash,5,"Msg $rmsg is of type $code";
  435. if ($code=~/Channel_sum.*/) {
  436. $rmsg =~ $OBIS_codes{$code};
  437. my $L= $hash->{helper}{Channels}{$channel} //$hash->{helper}{Channels}{$1} // "sum_$OBIS_channels{$1}" //$channel;
  438. if (AttrVal($name,"ignoreUnknown","off") eq "off" || $L ne $channel) {
  439. readingsBulkUpdate($hash, $L,(looks_like_number($3) ? $3+0 : $3).(AttrVal($name,"unitReadings","off") eq "off"?"":" $4"));
  440. readingsBulkUpdate($hash, "dir_$L",$hash->{helper}{directions}{$2} // $dir{$2}) if (length $2);
  441. }
  442. }
  443. elsif ($code=~/Channels.*/) {
  444. $rmsg =~ $OBIS_codes{$code};
  445. my $L=$hash->{helper}{Channels}{$channel} //$hash->{helper}{Channels}{$1} // $OBIS_channels{$1} //$channel;
  446. if (AttrVal($name,"ignoreUnknown","off") eq "off" || $L ne $channel) {
  447. readingsBulkUpdate($hash, "$L",(looks_like_number($3) ? $3+0 : $3).(AttrVal($name,"unitReadings","off") eq "off"?"":" $4"));
  448. readingsBulkUpdate($hash, "dir_$L",$hash->{helper}{directions}{$2} // $dir{$2}) if (length $2);
  449. }
  450. }
  451. elsif ($code=~/Counter.*/) {
  452. $rmsg =~ $OBIS_codes{$code};
  453. my $L=$hash->{helper}{Channels}{$channel} //$hash->{helper}{Channels}{$1.".".$2} // $OBIS_channels{$1.".".$2} // $channel;
  454. my $chan=$3+0 > 0 ? "_Ch$3" : "";
  455. if (AttrVal($name,"extChannels","off") eq "on") {$chan.=".$4" if $4;}
  456. if (AttrVal($name,"ignoreUnknown","off") eq "off" || $L ne $channel) {
  457. if($1==1) {
  458. Log3($hash,4,"Set ".$L.$chan." to ".((looks_like_number($3) ? $6+0 : $5) +AttrVal($name,"offset_energy",0)));
  459. readingsBulkUpdate($hash, $L.$chan ,(looks_like_number($3) ? $6+0 : $5) +AttrVal($name,"offset_energy",0).(AttrVal($name,"unitReadings","off") eq "off"?"":" $7"));
  460. } elsif ($1==2) {
  461. readingsBulkUpdate($hash, $L.$chan ,(looks_like_number($3) ? $6+0 : $5) +AttrVal($name,"offset_feed",0).(AttrVal($name,"unitReadings","off") eq "off"?"":" $7"));
  462. }
  463. readingsBulkUpdate($hash, "dir_$L",$hash->{helper}{directions}{$4} // $dir{$4}) if (length $4);
  464. }
  465. } else
  466. {
  467. $rmsg =~ $OBIS_codes{$code};
  468. my $data=$1;
  469. if($rmsg=~$SML_specialities{"HEX4"}[0]) {
  470. $data=$SML_specialities{"HEX4"}[1]->($data)
  471. } elsif($rmsg=~$SML_specialities{"HEX2"}[0]) {
  472. $data=$SML_specialities{"HEX2"}[1]->($data)
  473. } elsif($rmsg=~$SML_specialities{"TIME"}[0]) {
  474. $data=~/(\d+)/;
  475. $data=$SML_specialities{"TIME"}[1]->($1)
  476. }
  477. my $chan=$code//$OBIS_channels{$channel} //$channel;
  478. if ($#+ > 0) {
  479. $chan=$hash->{helper}{Channels}{$channel} // $hash->{helper}{Channels}{$1} // $OBIS_channels{$1} //$channel;
  480. } else {
  481. $chan=$hash->{helper}{Channels}{$channel} //$channel;
  482. }
  483. if (AttrVal($name,"ignoreUnknown","off") eq "off" || $chan ne $channel) {
  484. readingsBulkUpdate($hash, $chan ,$data); }
  485. }
  486. $found=1;
  487. last;
  488. }
  489. }
  490. }
  491. if ($found==0) {
  492. # Log 3,"Found a Channel-Attr";
  493. $rmsg=~/^((?:\d{1,3}-\d{1,3}:)?(?:\d{1,3}|[CF]).\d{1,3}(?:.\d{1,3})?(?:\*\d{1,3})?)(?:\((.*?)\))?\((.*?)\)/;
  494. my $chan=$hash->{helper}{Channels}{$channel} //$hash->{helper}{Channels}{$1} // $OBIS_channels{$1} //$channel;
  495. my $chan1=$chan;
  496. my $chan2=$chan."_2";
  497. # Log 3,"Setting $chan";
  498. my $v1=$3;
  499. my $v2;
  500. if (AttrVal($name,"valueBracket","second") eq "first") {
  501. $v1=length $2 ? $2 : $3;
  502. }
  503. if (AttrVal($name,"valueBracket","second") eq "both") {
  504. $v2=$2;
  505. ($v1,$v2)=($v2,$v1);
  506. if (!length $v1 and length $v2) {$v1=$v2;$v2=""}
  507. $chan1.="_1" if length $2;
  508. }
  509. if (AttrVal($name,"unitReadings","off") eq "off") {
  510. $v1=~s/(.*)\*.*/$1/;
  511. if ($v2) {$v2=~s/(.*)\*.*/$1/};
  512. }
  513. $v1+=0 if (looks_like_number($v1));
  514. $v2+=0 if (looks_like_number($v2));
  515. if (AttrVal($name,"ignoreUnknown","off") eq "off" || $chan ne $channel) {
  516. readingsBulkUpdate($hash, $chan1 ,$v1) if length $v1;
  517. readingsBulkUpdate($hash, $chan2 ,$v2) if length $v2;}
  518. }
  519. }
  520. }
  521. if ($hash->{helper}{EoM}==1) {last;}
  522. }
  523. $buffer = substr($buffer, index($buffer,chr(13).chr(10))+2);;
  524. }
  525. readingsEndUpdate($hash,1);
  526. }
  527. if (defined($remainingSML)) {$hash->{helper}{BUFFER}=$remainingSML}
  528. else{$hash->{helper}{BUFFER}=$buffer;}
  529. if($hash->{helper}{EoM}==1) { $hash->{helper}{BUFFER}="";}
  530. return $name;
  531. }
  532. #####################################
  533. sub OBIS_Ready($)
  534. {
  535. my ($hash) = @_;
  536. return DevIo_OpenDev($hash, 1, "OBIS_Init")
  537. if($hash->{STATE} eq "disconnected");
  538. my $name = $hash->{NAME};
  539. my $dev=$hash->{DeviceName};
  540. # This is relevant for windows/USB only
  541. my $po = $hash->{USBDev};
  542. my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags);
  543. return if (!$po);
  544. ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status;
  545. return ($InBytes>0);
  546. }
  547. sub OBIS_Attr(@)
  548. {
  549. my ($cmd,$name,$aName,$aVal) = @_;
  550. # $cmd can be "del" or "set"
  551. # $name is device name
  552. # aName and aVal are Attribute name and value
  553. my $hash = $defs{$name};
  554. my $dev=$hash->{DeviceName};
  555. #{$hash->{"test"}->{SetFn} = "OBIS_Set"}
  556. if ($cmd eq "del") {
  557. if ($aName eq "channels") { $hash->{helper}{Channels}=undef;}
  558. if ($aName eq "interval") {
  559. RemoveInternalTimer($hash);
  560. $hash->{helper}{DEVICES}[1]=0;
  561. }
  562. if ($aName eq "pollingMode") {
  563. delete($readyfnlist{"$name.$dev"});
  564. $selectlist{"$name.$dev"} = $hash;
  565. DevIo_CloseDev($hash);
  566. DevIo_OpenDev($hash, 0, "OBIS_Init");
  567. }
  568. }
  569. if ($cmd eq "set") {
  570. if ($aName eq "channels") {
  571. $hash->{helper}{Channels}=eval $aVal;
  572. if ($@) {
  573. Log3 $name, 3, "OBIS ($name) - X: Invalid regex in attr $name $aName $aVal: $@";
  574. $hash->{helper}{Channels}=undef;
  575. }
  576. }
  577. if ($aName eq "directions") {
  578. $hash->{helper}{directions}=eval $aVal;
  579. if ($@) {
  580. Log3 $name, 3, "OBIS ($name) - X: Invalid regex in attr $name $aName $aVal: $@";
  581. $hash->{helper}{directions}=undef;
  582. }
  583. }
  584. if ($aName eq "interval") {
  585. if ($aVal=~/^[1-9][0-9]*$/) {
  586. $hash->{helper}{TRIGGERTIME}=gettimeofday();
  587. RemoveInternalTimer($hash);
  588. $hash->{helper}{DEVICES}[1]=$aVal;
  589. my $t=OBIS_adjustAlign($hash,AttrVal($name,"alignTime",undef),$hash->{helper}{DEVICES}[1]);
  590. Log3 ($hash,5,"OBIS ($name) - Internal timer set to ".FmtDateTime($t)) if ($hash->{helper}{DEVICES}[1]>0);
  591. InternalTimer($t, "GetUpdate", $hash, 0) if ($hash->{helper}{DEVICES}[1]>0);
  592. } else {
  593. return "OBIS ($name) - $name: attr interval must be a number -> $aVal";
  594. }
  595. }
  596. if ($aName eq "alignTime") {
  597. if ($hash->{helper}{DEVICES}[1]>0 || !$init_done) {
  598. if ($aVal=~/\d+/) {
  599. RemoveInternalTimer($hash);
  600. $hash->{helper}{TRIGGERTIME}=gettimeofday();
  601. my $t=OBIS_adjustAlign($hash,$aVal,$hash->{helper}{DEVICES}[1]);
  602. Log3 ($hash,5,"OBIS ($name) - Internal timer set to ".FmtDateTime($t));
  603. InternalTimer($t, "GetUpdate", $hash, 0);
  604. } else {return "OBIS ($name): attr alignTime must be a Value >0"}
  605. } else {
  606. if ($init_done) {
  607. return "OBIS ($name): attr alignTime is useless, if no interval is specified";
  608. }
  609. }
  610. }
  611. if ($aName eq "pollingMode")
  612. {
  613. if ($aVal eq "on") {
  614. delete $hash->{FD};
  615. delete($selectlist{"$name.$dev"});
  616. $readyfnlist{"$name.$dev"} = $hash;
  617. } elsif ($aVal eq "off") {
  618. delete($readyfnlist{"$name.$dev"});
  619. $selectlist{"$name.$dev"} = $hash;
  620. DevIo_CloseDev($hash);
  621. DevIo_OpenDev($hash, 0, "OBIS_Init");
  622. }
  623. }
  624. }
  625. return undef;
  626. }
  627. sub OBIS_adjustAlign($$$)
  628. {
  629. my($hash, $attrVal, $interval) = @_;
  630. if (!$attrVal) {return gettimeofday()+$interval;}
  631. my ($alErr, $alHr, $alMin, $alSec, undef) = GetTimeSpec($attrVal);
  632. return "$hash->{NAME} alignTime: $alErr" if($alErr);
  633. my $tspec=strftime("\%H:\%M:\%S", gmtime($interval));
  634. # Obis_adjustAlignTimetest2($hash,AttrVal($hash->{NAME},"alignTime",undef),$hash->{helper}{DEVICES}[1]);
  635. my (undef, $hr, $min, $sec, undef) = GetTimeSpec($tspec);
  636. my $now = time();
  637. my $step = ($hr*60+$min)*60+$sec;
  638. my $alTime = ($alHr*60+$alMin)*60+$alSec;#-fhemTzOffset($now);
  639. my $ttime = int($hash->{helper}{TRIGGERTIME});
  640. my $off = ($ttime % 86400) - 86400;
  641. if ($off >= $alTime) {
  642. $ttime = gettimeofday();#-86400;
  643. $off -= 86400;
  644. }
  645. my $off2=$off;
  646. while($off < $alTime) {
  647. $off += $step;
  648. }
  649. $ttime += ($alTime-$off);
  650. $ttime += $step if($ttime <= $now);
  651. $hash->{NEXT} = FmtDateTime($ttime);
  652. $hash->{helper}{TRIGGERTIME} = ($off2<=$alTime) ? $ttime : (gettimeofday());
  653. return $hash->{helper}{TRIGGERTIME};
  654. }
  655. sub OBIS_hex2int {
  656. my ($hexstr) = @_;
  657. return 0
  658. if $hexstr !~ /^[0-9A-Fa-f]{1,35}$/;
  659. my $num = hex($hexstr);
  660. return $num >> 31 ? $num - 2 ** 32 : $num;
  661. }
  662. sub OBIS_CRC16($$) {
  663. my ($hash,$buff)=@_;
  664. my @crc16 = ( 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48,
  665. 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c,
  666. 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210,
  667. 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
  668. 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef,
  669. 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5,
  670. 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3,
  671. 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d,
  672. 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387,
  673. 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862,
  674. 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52,
  675. 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
  676. 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e,
  677. 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402,
  678. 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5,
  679. 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df,
  680. 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5,
  681. 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3,
  682. 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d,
  683. 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78);
  684. my $crc=0xFFFF;
  685. my $a=substr($buff,0,-2);
  686. my $b=substr($buff,-2);
  687. my $crc2=OBIS_hex2int(uc(unpack('H*',$b)));
  688. foreach (split //, $a) {
  689. $crc = ($crc >> 8) ^ $crc16[($crc ^ ord($_)) & 0xff];
  690. }
  691. $crc ^= 0xffff;
  692. $crc = (($crc & 0xff) << 8) | (($crc & 0xff00) >> 8);
  693. return $crc2==$crc ? 1 : 0;
  694. }
  695. ###############################################
  696. # Input: Whole Datastream, inkl. TL-Byte #
  697. # Output: Length, Type, Value, remaining Data #
  698. ###############################################
  699. sub OBIS_decodeTL($){
  700. my ($msg)=@_;
  701. my $msgLength=0;
  702. my $msgType=0;
  703. my $lt="";
  704. my $tmp="";
  705. # Log 3,"In: $msg";
  706. if ($msg) {
  707. $msgType =hex(substr($msg,0,2)) & 0b01110000;
  708. do {
  709. $lt=hex(substr($msg,0,2));
  710. $msg=substr($msg,2);
  711. $msgLength=($msgLength*16) + ($lt & 0b00001111);
  712. } while ($lt & 0b10000000);
  713. # Log 3,"Calculated length: $msgLength";
  714. if ($msgType == 0b01110000) {
  715. for (my $i=0;$i<$msgLength;$i++) {
  716. my $tmp2="";
  717. # Log 3,"--> $msg";
  718. (undef,undef,undef,$msg,$tmp2)=OBIS_decodeTL($msg);
  719. # Log 3,"<-- $tmp2 $msg";
  720. $tmp.=$tmp2;
  721. }
  722. $msgLength-=1;
  723. }
  724. $msgLength-=1;
  725. my $valu;
  726. if (length($msg)>$msgLength*2) {
  727. $valu=substr($msg,0,$msgLength*2);
  728. $tmp.=$valu;
  729. $msg=substr($msg,$msgLength*2);
  730. }
  731. # Log 3," Split Msg: $tmp $msg";
  732. return $msgLength,$msgType,$valu,$msg,$tmp;
  733. };
  734. }
  735. "Cogito, ergo sum.";
  736. =pod
  737. =item device
  738. =item summary Collects data from Smartmeters that report in OBIS-Standard
  739. =item summary_DE Wertet Smartmeter aus, welche ihre Daten im OBIS-Standard senden
  740. =begin html
  741. <a name="OBIS"></a>
  742. <h3>OBIS</h3>
  743. <ul>
  744. This module is for SmartMeters, that report their data in OBIS-Standard. It dosen't matter, wether the data comes as PlainText or SML-encoded.
  745. <br><br>
  746. <b>Define</b>
  747. <ul>
  748. <code>define &lt;name&gt; OBIS device|none [MeterType] </code><br>
  749. <br>
  750. &lt;device&gt; specifies the serial port to communicate with the smartmeter.
  751. Normally on Linux the device will be named /dev/ttyUSBx, where x is a number.
  752. For example /dev/ttyUSB0. You may specify the baudrate used after the @ char.<br>
  753. <br>
  754. Optional: MeterType can be of
  755. <ul><li>VSM102 -&gt; Voltcraft VSM102</li>
  756. <li>E110 -&gt; Landis&&;Gyr E110</li>
  757. <li>E350USB -&gt; Landis&&;Gyr E350 USB-Version</li>
  758. <li>Standard -&gt; Data comes as plainText</li>
  759. <li>SML -&gt; Smart Message Language</li></ul>
  760. <br>
  761. Example: <br>
  762. <code>define myPowerMeter OBIS /dev/ttyPlugwise@@9600,7,E,1 VSM102</code>
  763. <br>
  764. <br>
  765. </ul>
  766. <b>Attributes</b>
  767. <ul><li>
  768. <code>offset_feed <br>offset_energy</code><br>
  769. If your smartmeter is BEHIND the meter of your powersupplier, then you can hereby adjust
  770. the total-reading of your SM to that of your official one.
  771. </li><li>
  772. <code>channels</code><br>
  773. With this, you can rename the reported channels.<BR>e.g.:
  774. <code>attr myOBIS channels {"1.0.96.5.5.255"=>"Status","1.0.0.0.0.255"=>"Info","16.7"=>"Verbrauch"}></code>
  775. </li><li>
  776. <code>directions</code><br>
  777. Some Meters report feeding/comnsuming of power in a statusword.
  778. If this is set, you get an extra reading dir_total_consumption which defaults to "in" and "out".<BR>
  779. Here, you can change this text with, e.g.:
  780. <code>attr myOBIS directions {">" => "pwr consuming", "<"=>"pwr feeding"}</code>
  781. </li><li>
  782. <code>interval</code><br>
  783. The polling-interval in seconds. (Only useful in Polling-Mode)
  784. </li><li>
  785. <code>alignTime</code><br>
  786. Aligns the intervals to a given time. Each interval is repeatedly calculated.
  787. So if alignTime=00:00 and interval=600 aligns the interval to xx:00:00, xx:10:00, xx:20:00 etc....
  788. </li><li>
  789. <code>pollingMode</code><br>
  790. Changes from direct-read to polling-mode.
  791. Useful with meters, that send a continous datastream.
  792. Reduces CPU-load.
  793. </li><li>
  794. <code>unitReadings</code><br>
  795. Adds the units to the readings like w, wH, A etc.
  796. </li><li>
  797. <code>valueBracket</code><br>
  798. Sets, weather to use the value from the first or the second bracket, if applicable.
  799. Standard is "second"
  800. </li>
  801. <br>
  802. </ul></ul>
  803. =end html
  804. =begin html_DE
  805. <a name="OBIS"></a>
  806. <h3>OBIS</h3>
  807. <ul>
  808. Modul für Smartmeter, die ihre Daten im OBIS-Standard senden. Hierbei ist es egal, ob die Daten als reiner Text oder aber SML-kodiert kommen.
  809. <br><br>
  810. <b>Define</b>
  811. <ul>
  812. <code>define &lt;name&gt; OBIS device|none [MeterType] </code><br>
  813. <br>
  814. &lt;device&gt; gibt den seriellen Port an.
  815. <br><br>
  816. Optional: MeterType kann sein:
  817. <ul><li>VSM102 -&gt; Voltcraft VSM102</li>
  818. <li>E110 -&gt; Landis&&;Gyr E110</li>
  819. <li>E350USB -&gt; Landis&&;Gyr E350 USB-Version</li>
  820. <li>Standard -&gt; Daten kommen als plainText</li>
  821. <li>SML -&gt; Smart Message Language</li></ul>
  822. <br>
  823. Beispiel: <br>
  824. <code>define myPowerMeter OBIS /dev/ttyPlugwise@@9600,7,E,1 VSM102</code>
  825. <br>
  826. <br>
  827. </ul>
  828. <b>Attribute</b>
  829. <ul><li>
  830. <code>offset_feed <br>offset_energy</code><br>
  831. Wenn das Smartmeter hinter einem Zähler des EVU's sitzt, kann hiermit der Zähler des
  832. Smartmeters an den des EVU's angepasst werden.
  833. </li><li>
  834. <code>channels</code><br>
  835. Hiermit können die einzelnen Kanal-Readings mittels RegExes umbenannt werden.<BR>
  836. Beispiel: <code>attr myOBIS channels {"1.0.96.5.5.255"=>"Status","1.0.0.0.0.255"=>"Info","16.7"=>"Verbrauch"}></code>
  837. </li><li>
  838. <code>directions</code><br>
  839. Manche SmartMeter senden im Statusbyte die Stromrichtung.
  840. In diesem Fall gibt es ein extra Reading "dir_total_consumption" welches standardmäßig "in" and "out" beinhaltet<BR>
  841. Hiermit kann dieser Text geändert werden, z.B.:
  842. <code>attr myOBIS directions {">" => "pwr consuming", "<"=>"pwr feeding"}</code>
  843. </li><li>
  844. <code>interval</code><br>
  845. Abrufinterval der Daten. (Bringt nur im Polling-Mode was)
  846. </li><li>
  847. <code>algignTime</code><br>
  848. Richtet den Zeitpunkt von <interval> nach einer bestimmten Uhrzeit aus.
  849. </li><li>
  850. <code>pollingMode</code><br>
  851. Hiermit wird von Direktbenachrichtigung auf Polling umgestellt.
  852. Bei Smartmetern, welche von selbst im Sekundentakt senden,
  853. kann das zu einer spürbaren Senkung der Prozessorleistung führen.
  854. </li><li>
  855. <code>unitReadings</code><br>
  856. Hängt bei den Readings auch die Einheiten an, zB w, wH, A usw.
  857. </li><li>
  858. <code>valueBracket</code><br>
  859. Legt fest, ob der Wert aus dem ersten oder zweiten Klammernpaar genommen wird.
  860. Standard ist "second"
  861. </li>
  862. <br>
  863. </ul></ul>
  864. =end html_DE
  865. =cut