diff --git a/fhem/FHEM/47_OBIS.pm b/fhem/FHEM/47_OBIS.pm new file mode 100644 index 000000000..8129158de --- /dev/null +++ b/fhem/FHEM/47_OBIS.pm @@ -0,0 +1,369 @@ +############################################### +# +# 47_OBIS.pm +# +# +# $Id$ + +package main; +use strict; +use warnings; +use Time::HiRes qw(gettimeofday usleep); + +my $buffer = ""; +my $Zeit = 0; +my $speed=0; +my %channels = ( "21"=>"energy_L1", + "41"=>"energy_L2", + "61"=>"energy_L3", + "32"=>"voltage_L1", + "52"=>"voltage_L2", + "72"=>"voltage_L3", + "31"=>"power_L1", + "51"=>"power_L2", + "71"=>"power_L3", + "2"=>"feed_L1", + "4"=>"feed_L2", + "6"=>"feed_L3", + "1"=>"energy_current"); + +my %devices ; + +##################################### +sub OBIS_Initialize($) +{ + my ($hash) = @_; + + require "$attr{global}{modpath}/FHEM/DevIo.pm"; + + $hash->{Match} = ".*"; + $hash->{ReadFn} = "OBIS_Read"; + $hash->{ReadyFn} = "OBIS_Ready"; + $hash->{DefFn} = "OBIS_Define"; + $hash->{ParseFn} = "OBIS_Parse"; + $hash->{UndefFn} = "OBIS_Undef"; + $hash->{AttrFn} = "OBIS_Attr"; + $hash->{AttrList}= "do_not_notify:1,0 interval offset_feed offset_energy IODev channels ". + $readingFnAttributes; + Log3 $hash,3,"Initialize done..."; +} + +##################################### +sub OBIS_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + return 'wrong syntax: define OBIS devicename@baudrate[,databits,parity,stopbits]|none [MeterType]' + if(@a < 3); + + Log3 $hash,3,"Define called..."; + DevIo_CloseDev($hash); + RemoveInternalTimer($hash); + my $name = $a[0]; + my $dev = $a[2]; + my $type = $a[3]//"Unknown"; + $hash->{DeviceName} = $dev; + $hash->{MeterType}=$type if (defined($type)); + my $device_name = "OBIS_".$name; + $modules{OBIS}{defptr}{$device_name} = $hash; + Log3 $hash,4,"Starting $name with Device $dev (Type $type)."; + + if($dev eq "none") { + AssignIoPort($hash); + Log3 ($hash,1, "OBIS device is none, commands will be echoed only"); + return undef; + } + my $baudrate; + my $devi; + ($devi, $baudrate) = split("@", $dev); + if($baudrate =~ m/(\d+)(,([78])(,([NEO])(,([012]))?)?)?/) { + $baudrate = $1 if(defined($1)); + } + if ($baudrate==300) {$speed="0"} + if ($baudrate==600) {$speed="1"} + if ($baudrate==1200) {$speed="2"} + if ($baudrate==2400) {$speed="3"} + if ($baudrate==4800) {$speed="4"} + if ($baudrate==9600) {$speed="5"} + + %devices = ( +# Name, Init-String, repeat,2ndInit + "Unknown"=>[ "", -1,""], + "MT681"=>[ "", -1,""], + # Voltcraft VSM 102 + "VSM102"=>["/?!".chr(13).chr(10), 1,chr(6)."0".$speed."0".chr(13).chr(10)], + # Landis & Gyr E110 + "E110"=>["/?!".chr(13).chr(10), 1,chr(6)."0".$speed."0".chr(13).chr(10)], + ); + Log3 $hash,4,"Baudrate is $baudrate"; + + InternalTimer(gettimeofday()+2, "GetUpdate", $hash, 0) if (exists $devices{$type}); + Log3 ($hash,4,"Internal timer set.") if (exists $devices{$type}); + + Log3 $hash,4,"Opening device..."; + return DevIo_OpenDev($hash, 0, "OBIS_Init"); +} + +# Update-Routine +sub GetUpdate($) +{ + my ($hash) = @_; + my $name = $hash->{NAME}; + my $type= $hash->{MeterType}; + if (!exists $devices{$type}) {return undef;} + DevIo_SimpleWrite($hash,$devices{$type}[0],undef) ; + InternalTimer(gettimeofday()+AttrVal($name,"interval",600), "GetUpdate", $hash, 1) if ($devices{$type}[1]!=-1); +} + +sub OBIS_Init($) +{ + #nothing here yet + my ($hash) = @_; + my $name = $hash->{NAME}; + my $type= $hash->{MeterType}; + my $dev= $hash->{DeviceName}; + ($dev, undef) = split("@", $dev); + Log 4,"Device opened and initialized..."; + return undef; +} +##################################### +sub OBIS_Undef($$) +{ + my ($hash, $arg) = @_; + RemoveInternalTimer($hash); + DevIo_CloseDev($hash) if $hash->{DeviceName} != "none"; + return undef; +} + +##################################### +sub OBIS_Read($) +{ + my ($hash) = @_; + my $name = $hash->{NAME}; + my $tn = TimeNow(); + + my $buf = DevIo_SimpleRead($hash); + OBIS_Parse($hash,$buf); + return(undef); +} + +sub OBIS_Parse($$) +{ + my ($hash, $buf) = @_; + $buffer .= $buf; + return undef if(index($buffer,chr(13).chr(10)) == -1); + my $type= $hash->{MeterType}; + my $name = $hash->{NAME}; + readingsBeginUpdate($hash); + while(index($buffer,chr(13).chr(10)) ne -1) + { + my $rmsg=""; + $rmsg = substr($buffer, 0, index($buffer,chr(13).chr(10))); + Log3 $hash,5,"Msg-Parse: $rmsg"; + if ($rmsg=~ /.*\/(.*)/) { + DevIo_SimpleWrite($hash,$devices{$type}[2],undef) if (!$devices{$type}[2] eq ""); + } + + if($rmsg=~/^([23456789]+)-.*/) { + Log3 $hash,3,"Unknown OBIS-Message, please report: $rmsg"; + } +# Summe eingespeister Phase: 1-0:2.1.7*255(07568.01*kWh) + if ($rmsg=~ /1-0:([246])\.1\.7\*255\((-?\d+\.?\d*).*/) { + my $L=$1; + if (exists $channels{$1}) {$L="sum_".$channels{$1}} else {$L="Unknown_Channel"}; + Log3 $hash,5,"Set reading ".$L." to value ".($2+0); + readingsBulkUpdate($hash, $L,$2+0); + }; + +# Einspeisung und Bezug der einzelnen Phasen + if ($rmsg=~ /1-0:(\d+)\.7\.\d*\*\d*\((-?\d+\.?\d*).*/) { + my $L=$1; + if (exists $channels{$1}) {$L=$channels{$1}} else {$L="Unknown_Channel"}; + Log3 $hash,5,"Set reading ".$L." to value ".($2+0); + readingsBulkUpdate($hash, $L,$2+0); + }; + +# Seriennummer + if ($rmsg=~ /0-0:96\.1\.255\*255\((.*?)\).*/) { + Log3 $hash,5,"Set reading Serial to value ".$1; + readingsBulkUpdate($hash, "Serial" ,$1); } + +# Eigentumsnummer --> 1-0:0.0.0*255(GETTONE) + if ($rmsg=~ /1-0:0\.0\.0\*255\((.*?)\).*/) { + Log3 $hash,5,"Set reading Owner to value ".$1; + readingsBulkUpdate($hash, "Owner" ,$1); } + +# Statusbyte + if ($rmsg=~ /1-0:96\.5\.5\*255\((.*?)\).*/) { + Log3 $hash,5,"Set reading Status to value ".$1; + readingsBulkUpdate($hash, "Status" ,$1); } + +#Version + if ($rmsg=~ /\/(.*)/) { + Log3 $hash,5,"Set reading Version to value ".$1; + readingsBulkUpdate($hash, "Version" ,$1); } + +# Zählerstand --> 1-0:1.8.0*255(17483.88*kWh) + if ($rmsg=~ /1-0:([12])\.8\.\d\*255\((-?\d+\.?\d*).*/) { + if($1==1) { + Log3 $hash,5,"Set reading energy_total to value ".$2." with offset ".AttrVal($name,"offset_energy",0); + readingsBulkUpdate($hash, "energy_total" ,$2 +AttrVal($name,"offset_energy",0)); + } elsif ($1==2) { + Log3 $hash,5,"Set reading feed_total to value ".$2." with offset ".AttrVal($name,"offset_energy",0); + readingsBulkUpdate($hash, "feed_total" ,$2 +AttrVal($name,"offset_feed",0)); + } + } + $buffer = substr($buffer, index($buffer,chr(13).chr(10))+2);; + } + readingsEndUpdate($hash,1); + return $name; +} + +##################################### +sub OBIS_Ready($) +{ + my ($hash) = @_; + + return DevIo_OpenDev($hash, 1, "OBIS_Init") + if($hash->{STATE} eq "disconnected"); + + # This is relevant for windows/USB only + my $po = $hash->{USBDev}; + my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status; + return ($InBytes>0); +} + +sub OBIS_Attr(@) +{ + my ($cmd,$name,$aName,$aVal) = @_; + # $cmd can be "del" or "set" + # $name is device name + # aName and aVal are Attribute name and value + my $hash = $defs{$name}; + + if ($cmd eq "set") { + if ($aName eq "channels") { + %channels = eval $aVal; + if ($@) { + Log3 $name, 3, "X: Invalid regex in attr $name $aName $aVal: $@"; + } + } + if ($aName eq "interval") { + if ($aVal=~/^[1-9][0-9]*$/) { + RemoveInternalTimer($hash); + my $type= $hash->{MeterType}; + InternalTimer(gettimeofday()+2, "GetUpdate", $hash, 1) if ($devices{$type}[1]!=-1); + } else { + Log3 $name, 3, "$name: attr interval must be a number -> $aVal"; + } + } + + } + return undef; +} + +##################################### + +"Cogito, ergo sum."; + +=pod +=item device +=begin html + + +

OBIS

+ This module is for SmartMeter, that report thier data in OBIS-Standard. +
+ Define + define <name> OBIS device|none [MeterType]
+
+ <device> specifies the serial port to communicate with the smartmeter. + Normally on Linux the device will be named /dev/ttyUSBx, where x is a number. + For example /dev/ttyUSB0. You may specify the baudrate used after the @ char.
+

+ Optional:MeterType can be of + +
+ Example:
+ define myPowerMeter OBIS /dev/ttyPlugwise@@9600,7,E,1 VSM102 +
+
+ + Attributes + + +=end html + +=begin html + + + +

OBIS

+ Modul für Smartmeter, die ihre Daten im OBIS-Standard senden. +
+ Define + define <name> OBIS device|none [MeterType]
+
+ <device> gibt den seriellen Port an. +

+ Optional:MeterType kann sein: + +
+ Beispiel:
+ define myPowerMeter OBIS /dev/ttyPlugwise@@9600,7,E,1 VSM102 +
+
+ + Attribute + + +=end html_DE + +=cut