diff --git a/fhem/FHEM/88_xs1Bridge.pm b/fhem/FHEM/88_xs1Bridge.pm index 3e040e2cf..634390cc4 100644 --- a/fhem/FHEM/88_xs1Bridge.pm +++ b/fhem/FHEM/88_xs1Bridge.pm @@ -7,663 +7,684 @@ # - Port Check ??? # - Sendeausgabe im LOG anpassen # -# ################################################################# package main; # Laden evtl. abhängiger Perl- bzw. FHEM-Module -use HttpUtils; # um Daten via HTTP auszutauschen https://wiki.fhem.de/wiki/HttpUtils -use strict; -use warnings; # Warnings +use HttpUtils; # um Daten via HTTP auszutauschen https://wiki.fhem.de/wiki/HttpUtils +use strict; +use warnings; # Warnings use Net::Ping; -my $missingModul = ""; -my $xs1_ConnectionTry = 1; # disable Funktion sobald 10x keine Verbindung (Schutzabschaltung) +my $missingModul = ''; +my $xs1_ConnectionTry = 1; # disable Funktion sobald 10x keine Verbindung (Schutzabschaltung) my $xs1_id; -eval "use Encode qw(encode encode_utf8 decode_utf8);1" or $missingModul .= "Encode "; -eval "use JSON;1" or $missingModul .= "JSON "; -eval "use Net::Ping;1" or $missingModul .= "Net::Ping "; +eval {use Encode qw(encode encode_utf8 decode_utf8);1} or $missingModul .= 'Encode '; +eval {use JSON;1} or $missingModul .= 'JSON '; +eval {use Net::Ping;1} or $missingModul .= 'Net::Ping '; -#$| = 1; #Puffern abschalten, Hilfreich für PEARL WARNINGS Search +#$| = 1; #Puffern abschalten, Hilfreich für PEARL WARNINGS Search -sub xs1Bridge_Initialize($) { - my ($hash) = @_; - - $hash->{WriteFn} = "xs1Bridge_Write"; - $hash->{Clients} = ":xs1Dev:"; - $hash->{MatchList} = { "1:xs1Dev" => '[x][s][1][D][e][v][#][A][k][t][o][r]#[0-6][0-9].*|[x][s][1][D][e][v][#][S][e][n][s][o][r]#[0-6][0-9].*' }; ## https://regex101.com/ Testfunktion - - $hash->{DefFn} = "xs1Bridge_Define"; - $hash->{AttrFn} = "xs1Bridge_Attr"; - $hash->{UndefFn} = "xs1Bridge_Undef"; - $hash->{AttrList} = "debug:0,1,2 ". - "ignore:0,1 ". - "update_only_difference:0,1 ". - "view_Device_name:0,1 ". - "view_Device_function:0,1 ". - "xs1_blackl_aktor ". - "xs1_blackl_sensor ". - "xs1_control:0,1 ". - "xs1_interval:0,30,60,180,360 "; - ##$readingFnAttributes; ## die Standardattribute von FHEM - - foreach my $d(sort keys %{$modules{xs1Bridge}{defptr}}) { - my $hash = $modules{xs1Bridge}{defptr}{$d}; - } + +########################## +sub xs1Bridge_Initialize { + my ($hash) = @_; + + $hash->{WriteFn} = 'xs1Bridge_Write'; + $hash->{Clients} = ':xs1Dev:'; + $hash->{MatchList} = { '1:xs1Dev' => '[x][s][1][D][e][v][#][A][k][t][o][r]#[0-6][0-9].*|[x][s][1][D][e][v][#][S][e][n][s][o][r]#[0-6][0-9].*' }; ## https://regex101.com/ Testfunktion + + $hash->{DefFn} = 'xs1Bridge_Define'; + $hash->{AttrFn} = 'xs1Bridge_Attr'; + $hash->{UndefFn} = 'xs1Bridge_Undef'; + $hash->{AttrList} = 'debug:0,1,2 '. + 'ignore:0,1 '. + 'update_only_difference:0,1 '. + 'view_Device_function:0,1 '. + 'view_Device_name:0,1 '. + 'xs1_blackl_aktor '. + 'xs1_blackl_sensor '. + 'xs1_control:0,1 '. + 'xs1_interval:0,30,60,180,360 '; + ##$readingFnAttributes; ## die Standardattribute von FHEM + + foreach my $d(sort keys %{$modules{xs1Bridge}{defptr}}) { + $hash = $modules{xs1Bridge}{defptr}{$d}; + } + + return; } -sub xs1Bridge_Define($$) { - my ($hash, $def) = @_; - my @arg = split("[ \t][ \t]*", $def); - my $name = $hash->{NAME}; ## Der Definitionsname, mit dem das Gerät angelegt wurde. - my $typ = $hash->{TYPE}; ## Der Modulname, mit welchem die Definition angelegt wurde. - my $debug = AttrVal($hash->{NAME},"debug",0); - my $viewDeviceName = AttrVal($hash->{NAME},"view_Device_name",0); - my $viewDeviceFunction = AttrVal($hash->{NAME},"view_Device_function",0); - my $update_only_difference = AttrVal($hash->{NAME},"update_only_difference",0); - # 0 1 2 - return "Usage: define $name " if(@arg != 3); - #return "Usage: define $name " if(@arg != 4); - return "Your IP is not valid. Please Check!" if not($arg[2] =~ /[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}/s); - #return "Your PORT is not valid. Please Check!" if not($arg[3] =~ /[0-9]{2,5}/s); - return "Cannot define xs1Bridge device. Perl modul ${missingModul}is missing." if ( $missingModul ); +########################## +sub xs1Bridge_Define { + my ($hash, $def) = @_; + my @arg = split("[ \t][ \t]*", $def); + my $name = $hash->{NAME}; ## Der Definitionsname, mit dem das Gerät angelegt wurde. + my $typ = $hash->{TYPE}; ## Der Modulname, mit welchem die Definition angelegt wurde. + my $debug = AttrVal($hash->{NAME},'debug',0); + my $viewDeviceName = AttrVal($hash->{NAME},'view_Device_name',0); + my $viewDeviceFunction = AttrVal($hash->{NAME},'view_Device_function',0); + my $update_only_difference = AttrVal($hash->{NAME},'update_only_difference',0); - my $xs1check = 0; - if(!defined $modules{xs1Bridge}) { - my $p = Net::Ping->new("tcp", 2); - if(!($p->ping("$arg[2]", 2))) { - $xs1check = 1; - } - $p->close(); - return "Your IP is not reachable. Please Check!" if ($xs1check == 1); - } - - # Parameter Define - my $xs1_ip = $arg[2]; ## Zusatzparameter 1 bei Define - ggf. nur in Sub - $hash->{xs1_ip} = $xs1_ip; - - $hash->{STATE} = "Initialized"; ## Der Status des Modules nach Initialisierung. - $hash->{TIME} = time(); ## Zeitstempel, derzeit vom anlegen des Moduls - $hash->{VERSION} = "1.26"; ## Version - $hash->{BRIDGE} = 1; - - # Attribut gesetzt - $attr{$name}{xs1_interval} = "60" if( not defined( $attr{$name}{xs1_interval} ) ); - $attr{$name}{room} = "xs1" if( not defined( $attr{$name}{room} ) ); - $attr{$name}{xs1_control} = "0" if( not defined( $attr{$name}{xs1_control} ) ); - - $modules{xs1Bridge}{defptr}{BRIDGE} = $hash; - - InternalTimer(gettimeofday()+$attr{$name}{xs1_interval}, "xs1Bridge_GetUpDate", $hash); ## set Timer + # 0 1 2 + return "Usage: define $name " if(@arg != 3); + #return "Usage: define $name " if(@arg != 4); + return 'Your IP is not valid. Please Check!' if not($arg[2] =~ /[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}/s); + #return 'Your PORT is not valid. Please Check!' if not($arg[3] =~ /[0-9]{2,5}/s); + return "Cannot define xs1Bridge device. Perl modul ${missingModul}is missing." if ( $missingModul ); - #Log3 $name, 3, "$typ: IODev defined with xs1_ip: $xs1_ip"; - - if(!defined($defs{'FileLog_xs1Bridge'})) { ## Logfile existent check - Log3 $name, 4, "$typ: FileLog_xs1Bridge ist NICHT definiert"; - fhem("define FileLog_xs1Bridge FileLog ./log/xs1Bridge-%Y-%m.log ".$arg[0]); ## Logfile define - fhem("attr FileLog_xs1Bridge room xs1"); ## Logfile in xs1 room - } else { - Log3 $name, 4, "$typ: FileLog_xs1Bridge ist definiert"; - } + my $xs1check = 0; + if(!defined $modules{xs1Bridge}) { + my $p = Net::Ping->new('tcp', 2); + if(!($p->ping("$arg[2]", 2))) { + $xs1check = 1; + } + $p->close(); + return 'Your IP is not reachable. Please Check!' if ($xs1check == 1); + } - return undef; + # Parameter Define + my $xs1_ip = $arg[2]; ## Zusatzparameter 1 bei Define - ggf. nur in Sub + $hash->{xs1_ip} = $xs1_ip; + + $hash->{BRIDGE} = 1; + $hash->{STATE} = 'Initialized'; ## Der Status des Modules nach Initialisierung. + $hash->{TIME} = time(); ## Zeitstempel, derzeit vom anlegen des Moduls + $hash->{VERSION} = '1.27'; ## Version + + # Attribut gesetzt + if( not defined( $attr{$name}{xs1_control} ) ) { $attr{$name}{xs1_control} = '0'; } + if( not defined( $attr{$name}{xs1_interval} ) ) { $attr{$name}{xs1_interval} = '60'; } + + $modules{xs1Bridge}{defptr}{BRIDGE} = $hash; + + InternalTimer(gettimeofday()+$attr{$name}{xs1_interval}, 'xs1Bridge_GetUpDate', $hash); ## set Timer + + #Log3 $name, 3, "$typ: IODev defined with xs1_ip: $xs1_ip"; + + if(!defined($defs{'FileLog_xs1Bridge'})) { ## Logfile existent check + Log3 $name, 4, "$typ: FileLog_xs1Bridge ist NICHT definiert"; + fhem('define FileLog_xs1Bridge FileLog ./log/xs1Bridge-%Y-%m.log '.$arg[0]); ## Logfile define + } else { + Log3 $name, 4, "$typ: FileLog_xs1Bridge ist definiert"; + } + + return; } -sub xs1Bridge_Attr(@) { - my ($cmd,$name,$attrName,$attrValue) = @_; - my $hash = $defs{$name}; - my $typ = $hash->{TYPE}; - my $debug = AttrVal($hash->{NAME},"debug",0); - my $xs1_interval = 0; - - my @string_attrValue = split(",",$attrValue) if (defined $attrValue); ## for Check Blacklist - my $length = scalar @string_attrValue; ## for Check Blacklist - - # $cmd - Vorgangsart - kann die Werte "del" (löschen) oder "set" (setzen) annehmen - # $name - Gerätename - # $attrName/$attrValue sind Attribut-Name und Attribut-Wert - - #### Handling bei set .. attribute - if ($cmd eq "set") { - RemoveInternalTimer($hash); ## Timer löschen - Debug " $typ: Attr | Cmd:$cmd | RemoveInternalTimer" if($debug == 2); - if ($attrName eq "xs1_interval" && $attrValue == 0) { ## Handling xs1_interval == 0 - RemoveInternalTimer($hash); - readingsSingleUpdate($hash, "state", "deactive", 1); - }elsif ($attrName eq "xs1_interval" && $attrValue >= 30) { ## Handling xs1_interval >= 30 - $xs1_ConnectionTry = 1; - my $xs1_interval = $attrValue; - InternalTimer(gettimeofday()+$xs1_interval, "xs1Bridge_GetUpDate", $hash); - readingsSingleUpdate($hash, "state", "active", 1); - ### Ansicht xs1_Device_function ### - }elsif ($attrName eq "view_Device_function") { - if ($attrValue eq "1") { ## Handling view_Device_function 1 - #Log3 $name, 3, "$typ: Attribut view_Device_function $cmd to $attrValue"; - } - elsif ($attrValue eq "0") { ## Handling view_Device_function 0 - #Log3 $name, 3, "$typ: Attribut view_Device_function $cmd to $attrValue"; - } - ### Ansicht xs1_Device_name ### - }elsif ($attrName eq "view_Device_name") { - if ($attrValue eq "1") { ## Handling view_Device_name 1 - #Log3 $name, 3, "$typ: Attribut view_Device_name $cmd to $attrValue"; - } - elsif ($attrValue eq "0") { ## Handling view_Device_name 0 - #Log3 $name, 3, "$typ: Attribut view_Device_name $cmd to $attrValue"; - for my $i (0..64) { - delete $hash->{READINGS}{"Aktor_".sprintf("%02d", $i)."_name"} if($hash->{READINGS}); - delete $hash->{READINGS}{"Sensor_".sprintf("%02d", $i)."_name"} if($hash->{READINGS}); - } - } - ### Wertaenderung nur bei Difference ### - }elsif ($attrName eq "update_only_difference") { - if ($attrValue eq "1") { ## Handling update_only_difference 1 - #Log3 $name, 3, "$typ: Attribut update_only_difference $cmd to $attrValue"; - } - elsif ($attrValue eq "0") { ## Handling update_only_difference 0 - #Log3 $name, 3, "$typ: Attribut update_only_difference $cmd to $attrValue"; - for my $i (0..64) { - delete $hash->{READINGS}{"Aktor_".sprintf("%02d", $i)."_name"} if($hash->{READINGS}); - } - } - ### xs1 - steuern ### - }elsif ($attrName eq "xs1_control") { - if ($attrValue eq "1") { ## Handling xs1_control 1 - if(! $modules{xs1Dev}) { ## Check Modul vorhanden - $attr{$name}{xs1_control} = "0"; - return "Module xs1Dev is non-existent or still under development. Please wait" - } - } - ### Blacklist - Aktor / Sensor ### - }elsif ($attrName eq "xs1_blackl_aktor") { ## Handling xs1_blackl_aktor - for (my $x = 0; $x < $length ; $x++) { - if ($string_attrValue[$x] =~ /^[1-9]{1}\d*/ && $string_attrValue[$x] <65) { - ## RICHTIG ## - } else { - return "The comma separated value must only 1 to 64"; - } - } - Log3 $name, 4, "$typ: Attribut xs1_blackl_aktor $attrValue"; - - }elsif ($attrName eq "xs1_blackl_sensor") { ## Handling xs1_blackl_sensor - for (my $x = 0; $x < $length ; $x++) { - if ($string_attrValue[$x] =~ /^[1-9]{1}\d*/ && $string_attrValue[$x] <65) { - ## RICHTIG ## - } else { - return "The comma separated value must only 1 to 64"; - } - } - Log3 $name, 4, "$typ: Attribut xs1_blackl_sensor $attrValue"; - } - } - - #### Handling bei del ... attribute - if ($cmd eq "del") { - if ($attrName eq "xs1_interval") { ## Handling deleteattr xs1_interval - RemoveInternalTimer($hash); - readingsSingleUpdate($hash, "state", "deactive", 1); - Debug " $typ: Attr | Cmd:$cmd | $attrName" if($debug == 2); - } - elsif ($attrName eq "view_Device_function") { ## Handling deleteattr view_Device_function - Log3 $name, 3, "$typ: Attribut view_Device_function delete"; - for my $i (0..64) { - for my $i2 (1..4) { - delete $hash->{READINGS}{"Aktor_".sprintf("%02d", $i)."_function_".$i2} if($hash->{READINGS}); - } - } - } - elsif ($attrName eq "view_Device_name") { ## Handling deleteattr view_Device_name - Log3 $name, 3, "$typ: Attribut view_Device_name delete"; - for my $i (0..64) { - delete $hash->{READINGS}{"Aktor_".sprintf("%02d", $i)."_name"} if($hash->{READINGS}); - delete $hash->{READINGS}{"Sensor_".sprintf("%02d", $i)."_name"} if($hash->{READINGS}); - } - } - elsif ($attrName eq "update_only_difference") { - Log3 $name, 3, "$typ: Attribut update_only_difference delete"; - } - } - #### Handling bei state active - if ($hash->{STATE} eq "active") { - RemoveInternalTimer($hash); - InternalTimer(gettimeofday()+$xs1_interval, "xs1Bridge_GetUpDate", $hash); - Debug " $typ: Attr | RemoveInternalTimer + InternalTimer" if($debug == 2); - } - return undef; +########################## +sub xs1Bridge_Attr { + my ($cmd,$name,$attrName,$attrValue) = @_; + my $hash = $defs{$name}; + my $typ = $hash->{TYPE}; + my $debug = AttrVal($hash->{NAME},'debug',0); + my $xs1_interval = 0; + my @string_attrValue; + + @string_attrValue = split(',',$attrValue) if (defined $attrValue); ## for Check Blacklist + my $length = scalar @string_attrValue; ## for Check Blacklist + + #### Handling bei set .. attribute + if ($cmd eq 'set') { + RemoveInternalTimer($hash); ## Timer löschen + Debug " $typ: Attr | Cmd:$cmd | RemoveInternalTimer" if($debug == 2); + + ### xs1_interval == 0 ### + if ($attrName eq 'xs1_interval' && $attrValue == 0) { + RemoveInternalTimer($hash); + readingsSingleUpdate($hash, 'state', 'deactive', 1); + + ### xs1_interval >= 30 ### + } elsif ($attrName eq 'xs1_interval' && $attrValue >= 30) { + $xs1_ConnectionTry = 1; + $xs1_interval = $attrValue; + InternalTimer(gettimeofday()+$xs1_interval, 'xs1Bridge_GetUpDate', $hash); + readingsSingleUpdate($hash, 'state', 'active', 1); + + ### view_Device_function ### + } elsif ($attrName eq 'view_Device_function') { + if ($attrValue eq '1') { + #Log3 $name, 3, "$typ: Attribut view_Device_function $cmd to $attrValue"; + } elsif ($attrValue eq '0') { + #Log3 $name, 3, "$typ: Attribut view_Device_function $cmd to $attrValue"; + } + + ### view_Device_name ### + } elsif ($attrName eq 'view_Device_name') { + if ($attrValue eq '1') { + #Log3 $name, 3, "$typ: Attribut view_Device_name $cmd to $attrValue"; + } elsif ($attrValue eq '0') { + #Log3 $name, 3, "$typ: Attribut view_Device_name $cmd to $attrValue"; + for my $i (0..64) { + if ($hash->{READINGS}) { + if ($hash->{READINGS}{'Aktor_'.sprintf("%02d", $i).'_name'}) { delete $hash->{READINGS}{'Aktor_'.sprintf("%02d", $i).'_name'}; } + if ($hash->{READINGS}{'Sensor_'.sprintf("%02d", $i).'_name'}) { delete $hash->{READINGS}{'Sensor_'.sprintf("%02d", $i).'_name'}; } + } + } + } + + ### update_only_difference - Wertaenderung nur bei Difference ### + } elsif ($attrName eq 'update_only_difference') { + if ($attrValue eq '1') { + #Log3 $name, 3, "$typ: Attribut update_only_difference $cmd to $attrValue"; + } elsif ($attrValue eq '0') { + #Log3 $name, 3, "$typ: Attribut update_only_difference $cmd to $attrValue"; + for my $i (0..64) { + delete $hash->{READINGS}{'Aktor_'.sprintf("%02d", $i).'_name'} if($hash->{READINGS}); + } + } + + ### xs1 - steuern ### + } elsif ($attrName eq 'xs1_control') { + if ($attrValue eq '1') { + if(! $modules{xs1Dev}) { ## Check Modul vorhanden + $attr{$name}{xs1_control} = '0'; + return 'Module xs1Dev is non-existent or still under development. Please wait' + } + } + + ### Blacklist - Aktor / Sensor ### + } elsif ($attrName eq 'xs1_blackl_aktor') { + for (my $x = 0; $x < $length ; $x++) { + if ($string_attrValue[$x] =~ /^[1-9]{1}\d*/ && $string_attrValue[$x] <65) { + ## RICHTIG ## + } else { + return 'The comma separated value must only 1 to 64'; + } + } + Log3 $name, 4, "$typ: Attribut xs1_blackl_aktor $attrValue"; + + } elsif ($attrName eq 'xs1_blackl_sensor') { + for (my $x = 0; $x < $length ; $x++) { + if ($string_attrValue[$x] =~ /^[1-9]{1}\d*/ && $string_attrValue[$x] <65) { + ## RICHTIG ## + } else { + return 'The comma separated value must only 1 to 64'; + } + } + Log3 $name, 4, "$typ: Attribut xs1_blackl_sensor $attrValue"; + } + } + + #### Handling bei del ... attribute + if ($cmd eq 'del') { + if ($attrName eq 'xs1_interval') { + RemoveInternalTimer($hash); + readingsSingleUpdate($hash, 'state', 'deactive', 1); + Debug " $typ: Attr | Cmd:$cmd | $attrName" if($debug == 2); + + } elsif ($attrName eq 'view_Device_function') { + Log3 $name, 3, "$typ: Attribut view_Device_function delete"; + for my $i (0..64) { + for my $i2 (1..4) { + delete $hash->{READINGS}{'Aktor_'.sprintf("%02d", $i).'_function_'.$i2} if($hash->{READINGS}); + } + } + + } elsif ($attrName eq 'view_Device_name') { + Log3 $name, 3, "$typ: Attribut view_Device_name delete"; + for my $i (0..64) { + delete $hash->{READINGS}{'Aktor_'.sprintf("%02d", $i).'_name'} if($hash->{READINGS}); + delete $hash->{READINGS}{'Sensor_'.sprintf("%02d", $i).'_name'} if($hash->{READINGS}); + } + + } elsif ($attrName eq 'update_only_difference') { + Log3 $name, 3, "$typ: Attribut update_only_difference delete"; + } + } + + #### Handling bei state active + if ($hash->{STATE} eq 'active') { + RemoveInternalTimer($hash); + InternalTimer(gettimeofday()+$xs1_interval, 'xs1Bridge_GetUpDate', $hash); + Debug " $typ: Attr | RemoveInternalTimer + InternalTimer" if($debug == 2); + } + return; } -sub xs1Bridge_GetUpDate() { - my ($hash) = @_; - my $name = $hash->{NAME}; - my $typ = $hash->{TYPE}; - my $state = $hash->{STATE}; - my $xs1_ip = $hash->{xs1_ip}; - - my $xs1_uptimeStart = $hash->{helper}{xs1_uptimeStart}; - my $xs1_uptimeOld = $hash->{helper}{xs1_uptimeOld}; - my $xs1_uptimeNew = $hash->{helper}{xs1_uptimeNew}; - my $def; - - #http://x.x.x.x/control?callback=cname&cmd=... - #get_list_actuators - list all actuators i0 - #get_list_sensors - list all sensors i1 - #get_list_timers - list all timers i3 - #get_config_info - list all device info´s i2 - #get_protocol_info - list protocol info´s - - my $cmd = "/control?callback=cname&cmd="; - my @cmdtyp = ("get_list_actuators","get_list_sensors","get_config_info","get_list_timers","get_list_actuators"); - my @arrayname = ("actuator","sensor","info","timer","function"); - my @readingsname = ("Aktor","Sensor","","Timer",""); - - my $debug = AttrVal($hash->{NAME},"debug",0); - my $xs1_interval = AttrVal($name, "xs1_interval", 60); - my $viewDeviceName = AttrVal($hash->{NAME},"view_Device_name",0); - my $viewDeviceFunction = AttrVal($hash->{NAME},"view_Device_function",0); - my $update_only_difference = AttrVal($hash->{NAME},"update_only_difference",0); - my $xs1_control = AttrVal($hash->{NAME},"xs1_control",0); - my $xs1_blackl_aktor = AttrVal($hash->{NAME},"xs1_blackl_aktor",0); - my $xs1_blackl_sensor = AttrVal($hash->{NAME},"xs1_blackl_sensor",0); - - #### xs1Bridge xs1_interval >= 10 -> aktiviert zum auslesen - if ($xs1_interval >= 10 && $xs1_ConnectionTry <= 5) { - RemoveInternalTimer($hash); ## Timer löschen - InternalTimer(gettimeofday()+$xs1_interval, "xs1Bridge_GetUpDate", $hash); - Debug " -------------- ERROR CHECK - START --------------" if($debug == 2 || $debug == 1); - Debug " $typ: GetUpDate | RemoveInternalTimer + InternalTimer" if($debug == 2); - #Log3 $name, 3, "$typ: xs1Bridge_GetUpDate | RemoveInternalTimer + InternalTimer"; - if ($state eq "Initialized") { - readingsSingleUpdate($hash, "state", "active", 1); - } +########################## +sub xs1Bridge_GetUpDate { + my ($hash) = @_; + my $name = $hash->{NAME}; + my $typ = $hash->{TYPE}; + my $state = $hash->{STATE}; + my $xs1_ip = $hash->{xs1_ip}; - my $xs1Dev_check = "ERROR"; + my $def; + my $xs1_uptimeNew = $hash->{helper}{xs1_uptimeNew}; + my $xs1_uptimeOld = $hash->{helper}{xs1_uptimeOld}; + my $xs1_uptimeStart = $hash->{helper}{xs1_uptimeStart}; - #if($modules{xs1Dev} && $modules{xs1Dev}{LOADED}) { ## Check Modul vorhanden + geladen - if($modules{xs1Dev}) { ## Check Modul vorhanden - $xs1Dev_check = "ok"; - Debug " $typ: GetUpDate | Modul xs1Dev_check = $xs1Dev_check" if($debug == 2); - } else { - Debug " $typ: GetUpDate ERROR | Modul xs1Dev not existent! Please check it to be available!" if($debug == 2); - #Log3 $name, 3, "$typ: GetUpDate | xs1Dev_check = $xs1Dev_check"; - } + #http://x.x.x.x/control?callback=cname&cmd=... + #get_list_actuators - list all actuators i0 + #get_list_sensors - list all sensors i1 + #get_list_timers - list all timers i3 + #get_config_info - list all device info´s i2 + #get_protocol_info - list protocol info´s - #### JSON Abfrage - Schleife - for my $i (0..3) { - #### HTTP Requests #### Start #### - my $connection; - my $Http_err = ""; - my $Http_data = ""; - my $param = { - url => "http://".$xs1_ip.$cmd.$cmdtyp[$i], - timeout => 3, - method => "GET", # Lesen von Inhalten - }; + my $cmd = '/control?callback=cname&cmd='; + my @cmdtyp = ('get_list_actuators','get_list_sensors','get_config_info','get_list_timers','get_list_actuators'); + my @arrayname = ('actuator','sensor','info','timer','function'); + my @readingsname = ('Aktor','Sensor','','Timer',''); - #HttpUtils_BlockingGet($param); - ($Http_err, $Http_data) = HttpUtils_BlockingGet($param); - #### HTTP Requests #### END #### - - my $adress = "http://".$xs1_ip.$cmd.$cmdtyp[$i]; - my $json; - my $json_utf8; - my $decoded; + my $debug = AttrVal($hash->{NAME},'debug',0); + my $update_only_difference = AttrVal($hash->{NAME},'update_only_difference',0); + my $viewDeviceFunction = AttrVal($hash->{NAME},'view_Device_function',0); + my $viewDeviceName = AttrVal($hash->{NAME},'view_Device_name',0); + my $xs1_blackl_aktor = AttrVal($hash->{NAME},'xs1_blackl_aktor',0); + my $xs1_blackl_sensor = AttrVal($hash->{NAME},'xs1_blackl_sensor',0); + my $xs1_control = AttrVal($hash->{NAME},'xs1_control',0); + my $xs1_interval = AttrVal($name, 'xs1_interval', 60); - Debug " $typ: GetUpDate | Adresse: $adress | xs1_ConnectionTry=$xs1_ConnectionTry" if($debug == 1 && $Http_err eq ""); - Debug " $typ: GetUpDate | HTTP request: ".$Http_err."| xs1_ConnectionTry=$xs1_ConnectionTry" if($debug == 1 && $Http_err ne ""); + #### xs1Bridge xs1_interval >= 10 -> aktiviert zum auslesen + if ($xs1_interval >= 10 && $xs1_ConnectionTry <= 5) { + RemoveInternalTimer($hash); ## Timer löschen + InternalTimer(gettimeofday()+$xs1_interval, 'xs1Bridge_GetUpDate', $hash); + Debug ' -------------- ERROR CHECK - START --------------' if($debug == 2 || $debug == 1); + Debug " $typ: GetUpDate | RemoveInternalTimer + InternalTimer" if($debug == 2); + #Log3 $name, 3, "$typ: xs1Bridge_GetUpDate | RemoveInternalTimer + InternalTimer"; - #### HTTP Requests, ERROR - if ($Http_err ne "") { - # ERROR Message - # http://192.168.2.5/control?callback=cname&cmd=get_list_actuators: Can't connect(1) to http://192.168.2.5:80: IO::Socket::INET: connect: No route to host - # http://192.168.2.5/control?callback=cname&cmd=get_config_info: empty answer received - # http://192.168.2.5/control?callback=cname&cmd=get_config_info: Select timeout/error: - - #($Http_err) = $Http_err =~ /[:]\s.*/g; - Log3 $name, 3, "$typ: GetUpDate | Try=$xs1_ConnectionTry loop=$i | Error: ".$Http_err; - $xs1_ConnectionTry++; - last; ## Abbruch Schleife - } - #### HTTP Requests, OK dann ARRAY Verarbeitung - elsif ($Http_data ne "") { - ($json) = $Http_data =~ /[^(]*[}]/g; ## cut cname( + ) am Ende von Ausgabe -> ARRAY Struktur als Antwort vom xs1 - $json_utf8 = eval {encode_utf8( $json )}; ## UTF-8 character Bearbeitung, da xs1 TempSensoren ERROR - $decoded = eval {decode_json( $json_utf8 )}; - $xs1_ConnectionTry = 1; + if ($state eq 'Initialized') { + readingsSingleUpdate($hash, 'state', 'active', 1); + } - #### xs1 Aktoren / Sensoren als Readings - if ($i <= 1 ) { - my $xs1_data; - my @array; - - if (defined $decoded->{$arrayname[$i]}) { - @array = @{ $decoded->{$arrayname[$i]} }; - } else { - Log3 $name, 3, "$typ: GetUpDate | ARRAY-ERROR xs1 -> no Data in loop $i"; - last; - } + my $xs1Dev_check = 'ERROR'; - my $i3 = 0; ## Counter für real Werte in xs1, da sonst Verschiebungen wenn User ID´s verschiebt, NOTWENDIG! - - foreach my $f ( @array ) { - $i3++; - $xs1_id = $i3; - - #### Test ob Aktoren / Sensoren auf xs1_blackl - if ($f->{"type"} ne "disabled" && is_in_array($hash,$xs1_id,$i) == 0) { - my $xs1Dev = "xs1Dev"; + if($modules{xs1Dev}) { ## Check Modul vorhanden + $xs1Dev_check = 'ok'; + Debug " $typ: GetUpDate | Modul xs1Dev_check = $xs1Dev_check" if($debug == 2); + } else { + Debug " $typ: GetUpDate ERROR | Modul xs1Dev not existent! Please check it to be available!" if($debug == 2); + #Log3 $name, 3, "$typ: GetUpDate | xs1Dev_check = $xs1Dev_check"; + } - #### Aktoren spezifisch - my $xs1_function1 = "-"; - my $xs1_function2 = "-"; - my $xs1_function3 = "-"; - my $xs1_function4 = "-"; + #### JSON Abfrage - Schleife + for my $i (0..3) { + #### HTTP Requests #### Start #### + my $connection; + my $Http_err = ''; + my $Http_data = ''; + my $param = { + url => 'http://'.$xs1_ip.$cmd.$cmdtyp[$i], + timeout => 3, + method => 'GET', # Lesen von Inhalten + }; - if ($i == 0) { - #### xs1 Aktoren nur update bei differenten Wert - if ($update_only_difference == 1) { - my $oldState = ReadingsVal($name, $readingsname[$i]."_".sprintf("%02d", $i3), "unknown"); ## Readings Wert - my $newState = sprintf("%.1f" , $f->{"value"}); ## ARRAY Wert xs1 aktuell + #HttpUtils_BlockingGet($param); + ($Http_err, $Http_data) = HttpUtils_BlockingGet($param); + #### HTTP Requests #### END #### - Debug " $typ: ".$readingsname[$i]."_".sprintf("%02d", $i3)." oldState=$oldState newState=$newState" if($debug == 2); - - if ($oldState ne $newState) { - readingsSingleUpdate($hash, $readingsname[$i]."_".sprintf("%02d", $i3) , $newState, 0); - } - } + my $adress = 'http://'.$xs1_ip.$cmd.$cmdtyp[$i]; + my $decoded; + my $json; + my $json_utf8; - #### xs1 Aktoren / Funktion != disable - my @array2 = @{ $decoded->{'actuator'}->[$i3-1]->{$arrayname[4]} }; - my $i2 = 0; ## Funktionscounter + Debug " $typ: GetUpDate | Adresse: $adress | xs1_ConnectionTry=$xs1_ConnectionTry" if($debug == 1 && $Http_err eq ''); + Debug " $typ: GetUpDate | HTTP request: ".$Http_err."| xs1_ConnectionTry=$xs1_ConnectionTry" if($debug == 1 && $Http_err ne ''); - foreach my $f2 ( @array2 ) { - $i2++; + #### HTTP Requests, ERROR + if ($Http_err ne '') { + # ERROR Message + # http://192.168.2.5/control?callback=cname&cmd=get_list_actuators: Can't connect(1) to http://192.168.2.5:80: IO::Socket::INET: connect: No route to host + # http://192.168.2.5/control?callback=cname&cmd=get_config_info: empty answer received + # http://192.168.2.5/control?callback=cname&cmd=get_config_info: Select timeout/error: - #### xs1 Option - Ansicht Funktionsname - if ($viewDeviceFunction == 1) { - my $oldState = ReadingsVal($name, $readingsname[$i]."_".sprintf("%02d", $i3)."_".$arrayname[4]."_".$i2, "unknown"); ## Readings Wert - my $newState = $f2->{'type'}; ## ARRAY Wert xs1 aktuell + #($Http_err) = $Http_err =~ /[:]\s.*/g; + Log3 $name, 3, "$typ: GetUpDate | Try=$xs1_ConnectionTry loop=$i | Error: ".$Http_err; + $xs1_ConnectionTry++; + last; ## Abbruch Schleife - if ($oldState ne "unknown" && $newState eq "disabled") { ## FunktionReading del bei disable - Debug " $typ: "."Aktor_".sprintf("%02d", $i3)."_function_".$i2." are disabled" if($debug == 2); - delete $hash->{READINGS}{"Aktor_".sprintf("%02d", $i3)."_function_".$i2} if($hash->{READINGS}); - } + #### HTTP Requests, OK dann ARRAY Verarbeitung + } elsif ($Http_data ne '') { + ($json) = $Http_data =~ /[^(]*[}]/g; ## cut cname( + ) am Ende von Ausgabe -> ARRAY Struktur als Antwort vom xs1 + $json_utf8 = eval {encode_utf8( $json )}; ## UTF-8 character Bearbeitung, da xs1 TempSensoren ERROR + $decoded = eval {decode_json( $json_utf8 )}; + $xs1_ConnectionTry = 1; - if ($f2->{"type"} ne "disabled") { ## Funktion != function -> type disable - if ($oldState ne $newState) { - readingsSingleUpdate($hash, $readingsname[$i]."_".sprintf("%02d", $i3)."_".$arrayname[4]."_".$i2 , $f2->{"type"} , 1); - } - } - } + #### xs1 Aktoren / Sensoren als Readings + if ($i <= 1 ) { + my $xs1_data; + my @array; - #### Funktion != function -> type disable - if ($f2->{"type"} ne "disabled") { - if ($i2 == 1) { - $xs1_function1 = $f2->{"type"}; - }elsif ($i2 == 2) { - $xs1_function2 = $f2->{"type"}; - }elsif ($i2 == 3) { - $xs1_function3 = $f2->{"type"}; - }elsif ($i2 == 4) { - $xs1_function4 = $f2->{"type"}; - } - } - } - } + if (defined $decoded->{$arrayname[$i]}) { + @array = @{ $decoded->{$arrayname[$i]} }; + } else { + Log3 $name, 3, "$typ: GetUpDate | ARRAY-ERROR xs1 -> no Data in loop $i"; + last; + } - #### Value der Aktoren | Sensoren - if ($i == 1 || $i == 0 && $update_only_difference == 0) { # Aktoren | Sensoren im intervall - Format 0.0 bzw. 37.0 wie aus xs1 - readingsSingleUpdate($hash, $readingsname[$i]."_".sprintf("%02d", $i3) , sprintf("%.1f" , $f->{"value"}), 0); - $xs1_data = $xs1Dev."#".$readingsname[$i]."#".sprintf("%02d", $i3)."#".$f->{"type"}."#".sprintf("%.1f" , $f->{"value"})."#"."$xs1_function1"."#"."$xs1_function2"."#"."$xs1_function3"."#"."$xs1_function4"."#".$f->{"name"}; - } elsif ($i == 0 && $update_only_difference == 1){ # Aktoren | nur bei DIFF - Format 0.0 bzw. 37.0 wie aus xs1 - $xs1_data = $xs1Dev."#".$readingsname[$i]."#".sprintf("%02d", $i3)."#".$f->{"type"}."#".sprintf("%.1f" , $f->{"value"})."#"."$xs1_function1"."#"."$xs1_function2"."#"."$xs1_function3"."#"."$xs1_function4"."#".$f->{"name"}; - } + my $i3 = 0; ## Counter für real Werte in xs1, da sonst Verschiebungen wenn User ID´s verschiebt, NOTWENDIG! - #### Ausgaben je Typ unterschiedlich !!! - Debug " $typ: ".$readingsname[$i]."_".sprintf("%02d", $i3)." | ".$f->{"type"}." | ".$f->{"name"}." | ". $f->{"value"}." | "."F1 $xs1_function1 | F2 $xs1_function2 | F3 $xs1_function3 | F4 $xs1_function4" if($debug == 2 && $i == 0); - Debug " $typ: ".$readingsname[$i]."_".sprintf("%02d", $i3)." | ".$f->{"type"}." | ".$f->{"name"}." | ". $f->{"value"} if($debug == 2 && $i != 0); + foreach my $f ( @array ) { + $i3++; + $xs1_id = $i3; - ### Ansicht Namen der Aktoren | Sensoren als Readings - if ($viewDeviceName == 1) { - my $oldState = ReadingsVal($name, $readingsname[$i]."_".sprintf("%02d", $i3)."_name", "unknown"); ## Readings Wert - my $newState = $f->{"name"}; ## ARRAY Wert xs1 aktuell - - if ($oldState ne $newState) { ## Namen nur bei Änderung schreiben - #Log3 $name, 3, "$typ: GetUpDate | newState=$newState ne oldState=$oldState"; - readingsSingleUpdate($hash, $readingsname[$i]."_".sprintf("%02d", $i3)."_name" , $f->{"name"} , 1); - } - } - - ### Dispatch an xs1Device Modul - if ($xs1Dev_check eq "ok" && $xs1_control == 1) { - Debug " $typ: GetUpDate | Dispatch: $xs1_data" if($debug == 2); - Dispatch($hash,$xs1_data,undef) if($xs1_data); - } - } else { - #### ID bzw. Speicherplatz xs1 ist disabled | Reading are delete - delete $hash->{READINGS}{$readingsname[$i]."_".sprintf("%02d", $i3)} if($hash->{READINGS}); - delete $hash->{READINGS}{$readingsname[$i]."_".sprintf("%02d", $i3)."_name"} if($hash->{READINGS}); - - ### Erweiterung v1.20 ### Device | Logfile | SVG löschen wenn in xs1 disable - TEST - my $delDevice = "xs1Dev_".$readingsname[$i]."_".sprintf("%02d", $i3); - - if (defined($defs{"xs1Dev_".$readingsname[$i]."_".sprintf("%02d", $i3)})) { - #Log3 $name, 3, "$typ: GetUpDate | for delete $delDevice"; - fhem("delete ".$delDevice); ## delete Device - } - - if (defined($defs{"SVG_xs1Dev_".$readingsname[$i]."_".sprintf("%02d", $i3)})) { - #Log3 $name, 3, "$typ: GetUpDate | for delete FileLog_$delDevice"; - fhem("delete SVG_".$delDevice); ## delete FileLog_Device - } - - if (defined($defs{"FileLog_xs1Dev_".$readingsname[$i]."_".sprintf("%02d", $i3)})) { - #Log3 $name, 3, "$typ: GetUpDate | for delete FileLog_$delDevice"; - fhem("delete FileLog_".$delDevice); ## delete FileLog_Device - } - - ### Erweiterung v1.20 ### Device | Logfile | SVG löschen wenn in xs1 disable - TEST ### ENDE ### - - if ($i == 0) { - for my $count (1..4) { - delete $hash->{READINGS}{$readingsname[$i]."_".sprintf("%02d", $i3)."_function_".$count} if($hash->{READINGS}); - } - } - } - } - } - #### xs1 Info´s nur bei uptime Änderung als Readings - elsif ($i == 2) { - my $features; - my $features_i=0; + #### Test ob Aktoren / Sensoren auf xs1_blackl + if ($f->{'type'} ne 'disabled' && is_in_array($hash,$xs1_id,$i) == 0) { + my $xs1Dev = 'xs1Dev'; - my @xs1_readings = ("xs1_start","xs1_devicename","xs1_bootloader","xs1_hardware","xs1_features","xs1_firmware","xs1_mac","xs1_dhcp"); - my @xs1_decoded = (FmtDateTime(time()-($decoded->{'info'}{'uptime'})) , $decoded->{'info'}{'devicename'} , $decoded->{'info'}{'bootloader'} , $decoded->{'info'}{'hardware'} , $features , $decoded->{'info'}{'firmware'} , $decoded->{'info'}{'mac'} , $decoded->{'info'}{'autoip'}); - - my $oldState = ReadingsVal($name, $xs1_readings[0], "2000-01-01 03:33:33"); ## Readings Wert - my @oldstate = split (/[-,:,\s\/]/, $oldState); ## Split $year, $month, $mday, $hour, $min, $sec - $oldState = fhemTimeGm($oldstate[5], $oldstate[4], $oldstate[3], $oldstate[2], $oldstate[1]-1, $oldstate[0]-1900); ## Verarbeitung $sec, $min, $hour, $mday, $month-1, $year-1900 - - my $newState = FmtDateTime(time()-($decoded->{'info'}{'uptime'})); ## ARRAY uptime Wert xs1 aktuell - my @newState = split (/[-,:,\s\/]/, $newState); ## Split $year, $month, $mday, $hour, $min, $sec - $newState = fhemTimeGm($newState[5], $newState[4], $newState[3], $newState[2], $newState[1]-1, $newState[0]-1900); ## Verarbeitung $sec, $min, $hour, $mday, $month-1, $year-1900 - - #### Vergleich mit 5 Sekunden Tolleranz je Verarbeitungszeit Netzwerk | DLAN | CPU - if (abs($oldState - $newState) > 5) { - readingsBeginUpdate($hash); - for my $i2 (0..7) { - if ($i2 == 4) { - while (defined $decoded->{'info'}{'features'}->[$features_i]) { - $features.= $decoded->{'info'}{'features'}->[$features_i]." "; - $features_i++; - } - $xs1_decoded[4] = $features; ## ARRAY Wert xs1_decoded wird definiert - } - if (defined $xs1_decoded[$i2]) { - readingsBulkUpdate($hash, $xs1_readings[$i2] , $xs1_decoded[$i2]); - Debug " $typ: ".$xs1_readings[$i2].": ".$xs1_decoded[$i2] if($debug == 2); - } else { - Log3 $name, 3, "$typ: GetUpDate | ARRAY-ERROR xs1 -> no Data in loop $i|$i2"; - last; - } - } - readingsEndUpdate($hash, 1); - } - } - #### xs1 Timers als Readings - elsif ($i == 3) { - my @array = @{ $decoded->{$arrayname[$i]} }; - foreach my $f ( @array ) { - my $oldState = ReadingsVal($name, $readingsname[$i]."_".sprintf("%02d", $f->{"id"}), "unknown"); ## Readings Wert - my $newState = FmtDateTime($f->{"next"}); ## ARRAY Wert xs1 aktuell - - if ($f->{"type"} ne "disabled") { - if ($oldState ne $newState) { ## Update Reading nur bei Wertänderung - readingsSingleUpdate($hash, $readingsname[$i]."_".sprintf("%02d", $f->{"id"}) , FmtDateTime($f->{"next"}), 1); - } - Debug " $typ: ".$readingsname[$i]."_".sprintf("%02d", $f->{"id"})." | ".$f->{"name"}." | ".$f->{"type"}." | ". $f->{"next"} if($debug == 2); - } elsif ($oldState ne "unknown") { ## deaktive Timer mit Wert werden als Reading entfernt - Log3 $name, 3, "$typ: GetUpDate | ".$readingsname[$i]."_".sprintf("%02d", $f->{"id"})." is deactive in xs1"; - delete $defs{$name}{READINGS}{$readingsname[$i]."_".sprintf("%02d", $f->{"id"})}; - } - } - } - - if ($i < 2) { - Debug " --------------- ERROR CHECK - SUB --------------- " if($debug == 2); - } - ### Schleifen Ende ### - } - } + #### Aktoren spezifisch + my $xs1_function1 = '-'; + my $xs1_function2 = '-'; + my $xs1_function3 = '-'; + my $xs1_function4 = '-'; - Debug " ------------- ERROR CHECK - ALL END -------------\n " if($debug == 2 || $debug == 1); - } - - if ($xs1_ConnectionTry == 6) { ## Abschaltung xs1 nach 5 Verbindungsversuchen - $attr{$name}{xs1_interval} = "0"; - readingsSingleUpdate($hash, "state", "deactive", 1); - RemoveInternalTimer($hash); ## Timer löschen - Log3 $name, 3, "$typ: GetUpDate | connection ERROR -> xs1 set to disable! Device not reachable after 5 attempts"; - } + if ($i == 0) { + #### xs1 Aktoren nur update bei differenten Wert + if ($update_only_difference == 1) { + my $oldState = ReadingsVal($name, $readingsname[$i].'_'.sprintf("%02d", $i3), 'unknown'); ## Readings Wert + my $newState = sprintf("%.1f" , $f->{'value'}); ## ARRAY Wert xs1 aktuell + + Debug " $typ: ".$readingsname[$i].'_'.sprintf("%02d", $i3)." oldState=$oldState newState=$newState" if($debug == 2); + + if ($oldState ne $newState) { + readingsSingleUpdate($hash, $readingsname[$i].'_'.sprintf("%02d", $i3) , $newState, 0); + } + } + + #### xs1 Aktoren / Funktion != disable + my @array2 = @{ $decoded->{'actuator'}->[$i3-1]->{$arrayname[4]} }; + my $i2 = 0; ## Funktionscounter + + foreach my $f2 ( @array2 ) { + $i2++; + + #### xs1 Option - Ansicht Funktionsname + if ($viewDeviceFunction == 1) { + my $oldState = ReadingsVal($name, $readingsname[$i].'_'.sprintf("%02d", $i3).'_'.$arrayname[4].'_'.$i2, 'unknown'); ## Readings Wert + my $newState = $f2->{'type'}; ## ARRAY Wert xs1 aktuell + + if ($oldState ne 'unknown' && $newState eq 'disabled') { ## FunktionReading del bei disable + Debug " $typ: ".'Aktor_'.sprintf("%02d", $i3).'_function_'.$i2.' are disabled' if($debug == 2); + delete $hash->{READINGS}{'Aktor_'.sprintf("%02d", $i3).'_function_'.$i2} if($hash->{READINGS}); + } + + if ($f2->{'type'} ne 'disabled') { ## Funktion != function -> type disable + if ($oldState ne $newState) { + readingsSingleUpdate($hash, $readingsname[$i].'_'.sprintf("%02d", $i3).'_'.$arrayname[4].'_'.$i2 , $f2->{'type'} , 1); + } + } + } + + #### Funktion != function -> type disable + if ($f2->{'type'} ne 'disabled') { + if ($i2 == 1) { + $xs1_function1 = $f2->{'type'}; + } elsif ($i2 == 2) { + $xs1_function2 = $f2->{'type'}; + } elsif ($i2 == 3) { + $xs1_function3 = $f2->{'type'}; + } elsif ($i2 == 4) { + $xs1_function4 = $f2->{'type'}; + } + } + } + } + + #### Value der Aktoren | Sensoren + if ($i == 1 || $i == 0 && $update_only_difference == 0) { # Aktoren | Sensoren im intervall - Format 0.0 bzw. 37.0 wie aus xs1 + readingsSingleUpdate($hash, $readingsname[$i].'_'.sprintf("%02d", $i3) , sprintf("%.1f" , $f->{'value'}), 0); + $xs1_data = $xs1Dev.'#'.$readingsname[$i].'#'.sprintf("%02d", $i3).'#'.$f->{'type'}.'#'.sprintf("%.1f" , $f->{'value'}).'#'."$xs1_function1".'#'."$xs1_function2".'#'."$xs1_function3".'#'."$xs1_function4".'#'.$f->{'name'}; + } elsif ($i == 0 && $update_only_difference == 1){ # Aktoren | nur bei DIFF - Format 0.0 bzw. 37.0 wie aus xs1 + $xs1_data = $xs1Dev.'#'.$readingsname[$i].'#'.sprintf("%02d", $i3).'#'.$f->{'type'}.'#'.sprintf("%.1f" , $f->{'value'}).'#'."$xs1_function1".'#'."$xs1_function2".'#'."$xs1_function3".'#'."$xs1_function4".'#'.$f->{'name'}; + } + + #### Ausgaben je Typ unterschiedlich !!! + Debug " $typ: ".$readingsname[$i].'_'.sprintf("%02d", $i3).' | '.$f->{'type'}.' | '.$f->{'name'}.' | '. $f->{'value'}.' | '."F1 $xs1_function1 | F2 $xs1_function2 | F3 $xs1_function3 | F4 $xs1_function4" if($debug == 2 && $i == 0); + Debug " $typ: ".$readingsname[$i].'_'.sprintf("%02d", $i3).' | '.$f->{'type'}.' | '.$f->{'name'}.' | '. $f->{'value'} if($debug == 2 && $i != 0); + + ### Ansicht Namen der Aktoren | Sensoren als Readings + if ($viewDeviceName == 1) { + my $oldState = ReadingsVal($name, $readingsname[$i].'_'.sprintf("%02d", $i3).'_name', 'unknown'); ## Readings Wert + my $newState = $f->{'name'}; ## ARRAY Wert xs1 aktuell + + if ($oldState ne $newState) { ## Namen nur bei Änderung schreiben + #Log3 $name, 3, "$typ: GetUpDate | newState=$newState ne oldState=$oldState"; + readingsSingleUpdate($hash, $readingsname[$i].'_'.sprintf("%02d", $i3).'_name' , $f->{'name'} , 1); + } + } + + ### Dispatch an xs1Device Modul + if ($xs1Dev_check eq 'ok' && $xs1_control == 1) { + Debug " $typ: GetUpDate | Dispatch: $xs1_data" if($debug == 2); + Dispatch($hash,$xs1_data,undef) if($xs1_data); + } + + } else { + #### ID bzw. Speicherplatz xs1 ist disabled | Reading are delete + delete $hash->{READINGS}{$readingsname[$i].'_'.sprintf("%02d", $i3)} if($hash->{READINGS}); + delete $hash->{READINGS}{$readingsname[$i].'_'.sprintf('%02d', $i3).'_name'} if($hash->{READINGS}); + + ### Erweiterung v1.20 ### Device | Logfile | SVG löschen wenn in xs1 disable - TEST + my $delDevice = 'xs1Dev_'.$readingsname[$i].'_'.sprintf("%02d", $i3); + + if (defined($defs{'xs1Dev_'.$readingsname[$i].'_'.sprintf("%02d", $i3)})) { + #Log3 $name, 3, "$typ: GetUpDate | for delete $delDevice"; + fhem('delete '.$delDevice); ## delete Device + } + + if (defined($defs{'SVG_xs1Dev_'.$readingsname[$i].'_'.sprintf("%02d", $i3)})) { + #Log3 $name, 3, "$typ: GetUpDate | for delete FileLog_$delDevice"; + fhem('delete SVG_'.$delDevice); ## delete FileLog_Device + } + + if (defined($defs{'FileLog_xs1Dev_'.$readingsname[$i].'_'.sprintf("%02d", $i3)})) { + #Log3 $name, 3, "$typ: GetUpDate | for delete FileLog_$delDevice"; + fhem('delete FileLog_'.$delDevice); ## delete FileLog_Device + } + ### Erweiterung v1.20 ### Device | Logfile | SVG löschen wenn in xs1 disable - TEST ### ENDE ### + + if ($i == 0) { + for my $count (1..4) { + delete $hash->{READINGS}{$readingsname[$i].'_'.sprintf("%02d", $i3).'_function_'.$count} if($hash->{READINGS}); + } + } + } + } + + #### xs1 Info´s nur bei uptime Änderung als Readings + } elsif ($i == 2) { + my $features; + my $features_i=0; + + my @xs1_readings = ('xs1_start','xs1_devicename','xs1_bootloader','xs1_hardware','xs1_features','xs1_firmware','xs1_mac','xs1_dhcp'); + my @xs1_decoded = (FmtDateTime(time()-($decoded->{'info'}{'uptime'})) , $decoded->{'info'}{'devicename'} , $decoded->{'info'}{'bootloader'} , $decoded->{'info'}{'hardware'} , $features , $decoded->{'info'}{'firmware'} , $decoded->{'info'}{'mac'} , $decoded->{'info'}{'autoip'}); + + my $oldState = ReadingsVal($name, $xs1_readings[0], '2000-01-01 03:33:33'); ## Readings Wert + my @oldstate = split (/[-,:,\s\/]/, $oldState); ## Split $year, $month, $mday, $hour, $min, $sec + $oldState = fhemTimeGm($oldstate[5], $oldstate[4], $oldstate[3], $oldstate[2], $oldstate[1]-1, $oldstate[0]-1900); ## Verarbeitung $sec, $min, $hour, $mday, $month-1, $year-1900 + + my $newState = FmtDateTime(time()-($decoded->{'info'}{'uptime'})); ## ARRAY uptime Wert xs1 aktuell + my @newState = split (/[-,:,\s\/]/, $newState); ## Split $year, $month, $mday, $hour, $min, $sec + $newState = fhemTimeGm($newState[5], $newState[4], $newState[3], $newState[2], $newState[1]-1, $newState[0]-1900); ## Verarbeitung $sec, $min, $hour, $mday, $month-1, $year-1900 + + #### Vergleich mit 5 Sekunden Tolleranz je Verarbeitungszeit Netzwerk | DLAN | CPU + if (abs($oldState - $newState) > 5) { + readingsBeginUpdate($hash); + for my $i2 (0..7) { + if ($i2 == 4) { + while (defined $decoded->{'info'}{'features'}->[$features_i]) { + $features.= $decoded->{'info'}{'features'}->[$features_i].' '; + $features_i++; + } + $xs1_decoded[4] = $features; ## ARRAY Wert xs1_decoded wird definiert + } + if (defined $xs1_decoded[$i2]) { + readingsBulkUpdate($hash, $xs1_readings[$i2] , $xs1_decoded[$i2]); + Debug " $typ: ".$xs1_readings[$i2].': '.$xs1_decoded[$i2] if($debug == 2); + } else { + Log3 $name, 3, "$typ: GetUpDate | ARRAY-ERROR xs1 -> no Data in loop $i|$i2"; + last; + } + } + readingsEndUpdate($hash, 1); + } + + #### xs1 Timers als Readings + } elsif ($i == 3) { + my @array = @{ $decoded->{$arrayname[$i]} }; + foreach my $f ( @array ) { + my $oldState = ReadingsVal($name, $readingsname[$i].'_'.sprintf("%02d", $f->{'id'}), 'unknown'); ## Readings Wert + my $newState = FmtDateTime($f->{'next'}); ## ARRAY Wert xs1 aktuell + + if ($f->{'type'} ne 'disabled') { + if ($oldState ne $newState) { ## Update Reading nur bei Wertänderung + readingsSingleUpdate($hash, $readingsname[$i].'_'.sprintf("%02d", $f->{'id'}) , FmtDateTime($f->{'next'}), 1); + } + Debug " $typ: ".$readingsname[$i].'_'.sprintf("%02d", $f->{'id'}).' | '.$f->{'name'}.' | '.$f->{'type'}.' | '. $f->{'next'} if($debug == 2); + } elsif ($oldState ne 'unknown') { ## deaktive Timer mit Wert werden als Reading entfernt + Log3 $name, 3, "$typ: GetUpDate | ".$readingsname[$i].'_'.sprintf("%02d", $f->{'id'}).' is deactive in xs1'; + delete $defs{$name}{READINGS}{$readingsname[$i].'_'.sprintf("%02d", $f->{'id'})}; + } + } + } + + if ($i < 2) { + Debug ' --------------- ERROR CHECK - SUB --------------- ' if($debug == 2); + } + ### Schleifen Ende ### + } + } + + Debug " ------------- ERROR CHECK - ALL END -------------\n " if($debug == 2 || $debug == 1); + } + + if ($xs1_ConnectionTry == 6) { ## Abschaltung xs1 nach 5 Verbindungsversuchen + $attr{$name}{xs1_interval} = '0'; + readingsSingleUpdate($hash, 'state', 'deactive', 1); + RemoveInternalTimer($hash); ## Timer löschen + Log3 $name, 3, "$typ: GetUpDate | connection ERROR -> xs1 set to disable! Device not reachable after 5 attempts"; + } + + return; } -sub xs1Bridge_Write($) ## Zustellen von Daten via IOWrite() vom logischen zum physischen Modul -{ - my ($hash, $Aktor_ID, $xs1_typ, $cmd, $cmd2) = @_; - my $name = $hash->{NAME}; - my $typ = $hash->{TYPE}; - my $xs1_ip = $hash->{xs1_ip}; - my $debug = AttrVal($hash->{NAME},"debug",0); - - ## Anfrage (Client -> XS1): http://192.168.1.242/control?callback=cname&cmd=set_state_actuator&number=1&value=100 - - ## Aktor Typen aus xs1: (notwendig zur Verarbeitung) - ## ------------------------------------------------- - ## blind - Jalousie | dimmer - Dimmer | door - Tür | disabled - deaktivert - ## switch - Schalter | shutter - Rolladen | sound - Ton | sun-blind - Markise - ## temperature - Temperatur | timerswitch - Zeitschalter | window - Fenster - ## Sensor Typen (Auswahl) aus xs1: (nur Info) - ## ------------------------------------------ - ## alarmmat - Alarmmatte | disabled - deaktivert - ## gas_butan - Gasmelder Butan | gas_peak - Gas Spitzenwert - ## mail - Briefmelder | motion - Bewegung - ## other - Andere | presence - Anwesenheit - ## pwr_consump - Energiezähler | pwr_peak - Energie Spitzenwert - ## soilmoisture - Bodenfeuchte | soiltemp - Bodentemperatur - ## leafwetness - Blattfeuchte | remotecontrol - Fernbedienung - ## windowopen - Fenstermelder ... +########################## +sub xs1Bridge_Write { ## Zustellen von Daten via IOWrite() vom logischen zum physischen Modul + my ($hash, $Aktor_ID, $xs1_typ, $cmd, $cmd2) = @_; + my $name = $hash->{NAME}; + my $typ = $hash->{TYPE}; + my $xs1_ip = $hash->{xs1_ip}; + my $debug = AttrVal($hash->{NAME},'debug',0); - $Aktor_ID = substr($Aktor_ID, 1,2); + ## Anfrage (Client -> XS1): http://192.168.1.242/control?callback=cname&cmd=set_state_actuator&number=1&value=100 - my $xs1cmd; - #### xs1 Typ switch || shutter || timerswitch - Anpassung Sendebefehl - if ($xs1_typ eq "switch" || $xs1_typ eq "shutter" || $xs1_typ eq "timerswitch") { - $xs1cmd = "http://$xs1_ip/control?callback=cname&cmd=set_state_actuator&number=$Aktor_ID&$cmd2"; - } elsif ($xs1_typ eq "dimmer") { - if ($cmd eq "off") { - $cmd = 0; - } - $xs1cmd = "http://$xs1_ip/control?callback=cname&cmd=set_state_actuator&number=$Aktor_ID&$cmd2" if ($cmd2 =~ /[f][u][n][c][t][i][o][n][=]./); - my $valuenew = substr($cmd2,3,length($cmd2)-3) if ($cmd2 !~ /[f][u][n][c][t][i][o][n][=]./); - #Log3 $name, 3, "$typ: Write | Check cmd=$cmd cmd2=$cmd2 valuenew=$valuenew"; - $xs1cmd = "http://$xs1_ip/control?callback=cname&cmd=set_state_actuator&number=$Aktor_ID&value=$valuenew" if ($cmd2 !~ /[f][u][n][c][t][i][o][n][=]./); - } else { - #### keine Verarbeitung zum senden #### - Log3 $name, 3, "$typ: Write | $xs1_typ not control xs1. Please inform me!"; - last; - } + ## Aktor Typen aus xs1: (notwendig zur Verarbeitung) + ## ------------------------------------------------- + ## blind - Jalousie | dimmer - Dimmer | door - Tür | disabled - deaktivert + ## switch - Schalter | shutter - Rolladen | sound - Ton | sun-blind - Markise + ## temperature - Temperatur | timerswitch - Zeitschalter | window - Fenster - ### HTTP Requests #### Start #### - my $connection; - my $Http_err = ""; - my $Http_data; - my $param = { - url => "$xs1cmd", - timeout => 3, - method => "GET", # Lesen von Inhalten - }; + ## Sensor Typen (Auswahl) aus xs1: (nur Info) + ## ------------------------------------------ + ## alarmmat - Alarmmatte | disabled - deaktivert + ## gas_butan - Gasmelder Butan | gas_peak - Gas Spitzenwert + ## mail - Briefmelder | motion - Bewegung + ## other - Andere | presence - Anwesenheit + ## pwr_consump - Energiezähler | pwr_peak - Energie Spitzenwert + ## soilmoisture - Bodenfeuchte | soiltemp - Bodentemperatur + ## leafwetness - Blattfeuchte | remotecontrol - Fernbedienung + ## windowopen - Fenstermelder ... - ($Http_err, $Http_data) = HttpUtils_BlockingGet($param); - ### HTTP Requests #### END #### - - if ($Http_err ne "") { - ($Http_err) = $Http_err =~ /[:]\s.*/g; - Log3 $name, 3, "$typ: Write | no Control possible | Error".$Http_err; - return undef; - } elsif ($Http_data ne "") { - Debug " $typ: Write | Send to xs1 -> $xs1cmd" if($debug == 1); ## Kontrolle Sendebefehl - } + $Aktor_ID = substr($Aktor_ID, 1,2); + + my $xs1cmd; + my $valuenew; + + #### xs1 Typ switch || shutter || timerswitch - Anpassung Sendebefehl + if ($xs1_typ eq 'switch' || $xs1_typ eq 'shutter' || $xs1_typ eq 'timerswitch') { + $xs1cmd = "http://$xs1_ip/control?callback=cname&cmd=set_state_actuator&number=$Aktor_ID&$cmd2"; + } elsif ($xs1_typ eq 'dimmer') { + if ($cmd eq 'off') { + $cmd = 0; + } + $xs1cmd = "http://$xs1_ip/control?callback=cname&cmd=set_state_actuator&number=$Aktor_ID&$cmd2" if ($cmd2 =~ /[f][u][n][c][t][i][o][n][=]./); + $valuenew = substr($cmd2,3,length($cmd2)-3) if ($cmd2 !~ /[f][u][n][c][t][i][o][n][=]./); + #Log3 $name, 3, "$typ: Write | Check cmd=$cmd cmd2=$cmd2 valuenew=$valuenew"; + $xs1cmd = "http://$xs1_ip/control?callback=cname&cmd=set_state_actuator&number=$Aktor_ID&value=$valuenew" if ($cmd2 !~ /[f][u][n][c][t][i][o][n][=]./); + } else { + #### keine Verarbeitung zum senden #### + Log3 $name, 3, "$typ: Write | $xs1_typ not control xs1. Please inform me!"; + last; + } + + ### HTTP Requests #### Start #### + my $connection; + my $Http_err = ''; + my $Http_data; + my $param = { + url => "$xs1cmd", + timeout => 3, + method => 'GET', # Lesen von Inhalten + }; + + ($Http_err, $Http_data) = HttpUtils_BlockingGet($param); + ### HTTP Requests #### END #### + + if ($Http_err ne '') { + ($Http_err) = $Http_err =~ /[:]\s.*/g; + Log3 $name, 3, "$typ: Write | no Control possible | Error".$Http_err; + return; + } elsif ($Http_data ne '') { + Debug " $typ: Write | Send to xs1 -> $xs1cmd" if($debug == 1); ## Kontrolle Sendebefehl + } + + return; } -sub xs1Bridge_Undef($$) -{ - my ( $hash, $name) = @_; - my $typ = $hash->{TYPE}; - RemoveInternalTimer($hash); +########################## +sub xs1Bridge_Undef { + my ( $hash, $name) = @_; + my $typ = $hash->{TYPE}; - delete $modules{xs1Bridge}{defptr}{BRIDGE} if( defined($modules{xs1Bridge}{defptr}{BRIDGE}) ); - Log3 $name, 3, "$typ: deleting Device with Name $name"; - - foreach my $d (sort keys %defs) { - if(defined($defs{$d}) && defined($defs{$d}{IODev}) && $defs{$d}{IODev} == $hash) { - Log3 $name, 3, "$typ: deleting IODev for $d"; - delete $defs{$d}{IODev}; - } - } - - return undef; + RemoveInternalTimer($hash); + + delete $modules{xs1Bridge}{defptr}{BRIDGE} if( defined($modules{xs1Bridge}{defptr}{BRIDGE}) ); + Log3 $name, 3, "$typ: deleting Device with Name $name"; + + foreach my $d (sort keys %defs) { + if(defined($defs{$d}) && defined($defs{$d}{IODev}) && $defs{$d}{IODev} == $hash) { + Log3 $name, 3, "$typ: deleting IODev for $d"; + delete $defs{$d}{IODev}; + } + } + + return; } ########################## # eigene Sub -sub is_in_array($$$) -{ - my ( $hash,$xs1_id,$i) = @_; - my $name = $hash->{NAME}; - my $typ = $hash->{TYPE}; - - my $xs1_blackl = AttrVal($hash->{NAME},"xs1_blackl_aktor",0) if ($i eq 0); - $xs1_blackl = AttrVal($hash->{NAME},"xs1_blackl_sensor",0) if ($i eq 1); +sub is_in_array { + my ($hash,$xs1_id,$i) = @_; + my $name = $hash->{NAME}; + my $typ = $hash->{TYPE}; + my $xs1_blackl; - my @attr_array=split(/,/,$xs1_blackl); - if ( grep( /^$xs1_id$/, @attr_array ) ) { - #Log3 $name, 3, "$typ: is_in_array | id=$xs1_id auf xs1_blackl Aktoren" if ($i eq 0); - #Log3 $name, 3, "$typ: is_in_array | id=$xs1_id auf xs1_blackl Sensoren" if ($i eq 1); - return 1; - } else { - #Log3 $name, 3, "$typ: is_in_array | id=$xs1_id NICHT auf xs1_blackl Aktoren" if ($i eq 0); - #Log3 $name, 3, "$typ: is_in_array | id=$xs1_id NICHT auf xs1_blackl Sensoren" if ($i eq 1); - return 0; - } + if ($i == 0) { + $xs1_blackl = AttrVal($hash->{NAME},'xs1_blackl_aktor',0); + } elsif ($i == 1) { + $xs1_blackl = AttrVal($hash->{NAME},'xs1_blackl_sensor',0); + } + my @attr_array=split(/,/,$xs1_blackl); + if ( grep { /^$xs1_id$/ } @attr_array ) { + #Log3 $name, 3, "$typ: is_in_array | id=$xs1_id auf xs1_blackl Aktoren" if ($i eq 0); + #Log3 $name, 3, "$typ: is_in_array | id=$xs1_id auf xs1_blackl Sensoren" if ($i eq 1); + return 1; + } else { + #Log3 $name, 3, "$typ: is_in_array | id=$xs1_id NICHT auf xs1_blackl Aktoren" if ($i eq 0); + #Log3 $name, 3, "$typ: is_in_array | id=$xs1_id NICHT auf xs1_blackl Sensoren" if ($i eq 1); + return 0; + } } ########################## @@ -683,100 +704,105 @@ sub is_in_array($$$)

xs1Bridge

    - With this module you can read out the device xs1 from EZcontrol. There will be actors | Sensors | Timer | Information read from xs1 and written in readings. With each read only readings are created or updated, which are also defined and active in xs1. Actor | Sensor or timer definitions which are deactivated in xs1 are NOT read. -

    + With this module you can read out the device xs1 from EZcontrol. There will be actors | Sensors | Timer | Information read from xs1 and written in readings. With each read only readings are created or updated, which are also defined and active in xs1. Actor | Sensor or timer definitions which are deactivated in xs1 are NOT read. +

    - The module was developed based on the firmware version v4-Beta of the xs1. There may be errors due to different adjustments within the manufacturer's firmware.
    - Testet firmware: v4.0.0.5326 (Beta) @me | v3.0.0.4493 @ForumUser
    - -
      - Currently implemented types of xs1 for processing:
      -
    • Aktor: dimmer, switch, shutter, timerswitch
    • -
    • Sensor: barometer, counter, counterdiff, light, motion, other, rain, rain_1h, rain_24h, rainintensity, remotecontrol, uv_index, waterdetector, winddirection, windgust, windspeed, windvariance
    • -


    + The module was developed based on the firmware version v4-Beta of the xs1. There may be errors due to different adjustments within the manufacturer's firmware.
    + Testet firmware: v4.0.0.5326 (Beta) @me | v3.0.0.4493 @ForumUser

    +
      + Currently implemented types of xs1 for processing:
      +
    • Aktor: dimmer, switch, shutter, timerswitch
    • +
    • Sensor: barometer, counter, counterdiff, light, motion, other, rain, rain_1h, rain_24h, rainintensity, remotecontrol, uv_index, waterdetector, winddirection, windgust, windspeed, windvariance
    • +


    - - Define
    -
      - xs1 without password:  define <NAME> xs1Bridge <IP>
      - xs1 with password:       define <NAME> xs1Bridge <User>:<Passwort>@<IP> -

      + + Define
      +
        + xs1 without password:  define <NAME> xs1Bridge <IP>
        + xs1 with password:       define <NAME> xs1Bridge <User>:<Passwort>@<IP> +

        - The module can not create without the IP of the xs1. If the IP can not be reached during module definition, the Define process is aborted. -
          -
        • <IP> is IP address in the local network
        • -
        • <User> is the administrator user admin (default)
        • -
        • <Password> is the assigned administrator password in xs1
        • -

        - examples: -
          - define EZcontrol_xs1 xs1Bridge 192.168.1.45
          - define EZcontrol_xs1 xs1Bridge admin:secret@192.168.1.45
          -
        -

      - Set -
        N/A

      - Get
      -
        N/A

      - - Attributes -
        -
      • debug (0,1,2)
        - This brings the module into a very detailed debug output in the logfile. Program parts can be checked and errors checked.
        - (Default, debug 0) -

      • -
      • update_only_difference (0,1)
        - The actuators defined in xs1 are only updated when the value changes.
        - (Default, update_only_difference 0)

      • -
      • view_Device_name (0,1)
        - The actor names defined in xs1 are read as Reading.
        - (Default, view_Device_name 0)
        -

      • -
      • view_Device_function (0,1)
        - The actuator functions defined in xs1 are read out as Reading.
        - (Default, view_Device_function 0)
        -

      • -
      • xs1_blackl_aktor
        - A comma-separated blacklist of actuators, which should not be created automatically as soon as xs1_control = 1(aktiv).
        - (Example: 2,40,3)
        -

      • -
      • xs1_blackl_sensor
        - A comma-separated blacklist of sensors, which should not be created automatically as soon as xs1_control = 1(aktiv).
        - (Example: 3,37,55)
        -

      • -
      • xs1_control (0,1)
        - Option to control the xs1. After activating this, the xs1Dev module creates each actuator and sensor in FHEM.
        - (Default, xs1_control 0)

        -
      • xs1_interval (0,30,60,180,360)
        - This is the interval in seconds at which readings are read from xs1
        - For actuators, only different states are updated in the set interval.
        - Sensors are always updated in intervals, regardless of the status.
        - (Default, xs1_interval 60) -

      • -

        -
      - explanation: -
        -
      • various Readings:
      • -
          -
        • Aktor_(01-64)
        • defined actuator in the device
          -
        • Aktor_(01-64)_name
        • defined actor name in the device
          -
        • Aktor_(01-64)_function(1-4)
        • defined actuator function in the device
          -
        • Sensor_(01-64)
        • defined sensor in the device
          -
        • Sensor_(01-64)_name
        • defined sensor name in the device
          -
        • Timer_(01-128)
        • defined timer in the device
          -
        • xs1_bootloader
        • version of bootloader
          -
        • xs1_dhcp
        • DHCP on/off
          -
        • xs1_features
        • purchased feature when buying (A = send | B = receive | C = Skripte/Makros | D = Media Access)
          -
        • xs1_firmware
        • firmware number
          -
        • xs1_start
        • device start
          -

        -
      • The message "... Can't connect ..." or "ERROR: empty answer received" in the system logfile says that there was no query for a short time.
        - (This can happen more often with DLAN.)

      • -
      • If the device has not been connected after 5 connection attempts, the module will switch on < disable > !

      • -
      • Create logfile automatically after define | scheme: define FileLog_xs1Bridge FileLog ./log/xs1Bridge-%Y-%m.log <name>
        - The following values ​​are recorded in logfile: Timer | xs1-status information
      • -
      + The module can not create without the IP of the xs1. If the IP can not be reached during module definition, the Define process is aborted. +
        +
      • <IP> is IP address in the local network
      • +
      • <User> is the administrator user admin (default)
      • +
      • <Password> is the assigned administrator password in xs1
      • +

      + examples: +
        + define EZcontrol_xs1 xs1Bridge 192.168.1.45
        + define EZcontrol_xs1 xs1Bridge admin:secret@192.168.1.45
        +
      +

    + + Set +
      N/A

    + + Get
    +
      N/A

    + + + Attributes +
      +
    • debug (0,1,2)
      + This brings the module into a very detailed debug output in the logfile. Program parts can be checked and errors checked.
      + (Default, debug 0) +

    • +
    • update_only_difference (0,1)
      + The actuators defined in xs1 are only updated when the value changes.
      + (Default, update_only_difference 0) +

    • +
    • view_Device_name (0,1)
      + The actor names defined in xs1 are read as Reading.
      + (Default, view_Device_name 0)
      +

    • +
    • view_Device_function (0,1)
      + The actuator functions defined in xs1 are read out as Reading.
      + (Default, view_Device_function 0)
      +

    • +
    • xs1_blackl_aktor
      + A comma-separated blacklist of actuators, which should not be created automatically as soon as xs1_control = 1(aktiv).
      + (Example: 2,40,3)
      +

    • +
    • xs1_blackl_sensor
      + A comma-separated blacklist of sensors, which should not be created automatically as soon as xs1_control = 1(aktiv).
      + (Example: 3,37,55)
      +

    • +
    • xs1_control (0,1)
      + Option to control the xs1. After activating this, the xs1Dev module creates each actuator and sensor in FHEM.
      + (Default, xs1_control 0)

      +
    • xs1_interval (0,30,60,180,360)
      + This is the interval in seconds at which readings are read from xs1
      + For actuators, only different states are updated in the set interval.
      + Sensors are always updated in intervals, regardless of the status.
      + (Default, xs1_interval 60) +

    • +

      +
    + + explanation: +
      +
    • various Readings:
    • +
        +
      • Aktor_(01-64)
      • defined actuator in the device
        +
      • Aktor_(01-64)_name
      • defined actor name in the device
        +
      • Aktor_(01-64)_function(1-4)
      • defined actuator function in the device
        +
      • Sensor_(01-64)
      • defined sensor in the device
        +
      • Sensor_(01-64)_name
      • defined sensor name in the device
        +
      • Timer_(01-128)
      • defined timer in the device
        +
      • xs1_bootloader
      • version of bootloader
        +
      • xs1_dhcp
      • DHCP on/off
        +
      • xs1_features
      • purchased feature when buying (A = send | B = receive | C = Skripte/Makros | D = Media Access)
        +
      • xs1_firmware
      • firmware number
        +
      • xs1_start
      • device start
        +

      +
    • The message "... Can't connect ..." or "ERROR: empty answer received" in the system logfile says that there was no query for a short time.
      + (This can happen more often with DLAN.)

    • +
    • If the device has not been connected after 5 connection attempts, the module will switch on < disable > !

    • +
    • Create logfile automatically after define | scheme: define FileLog_xs1Bridge FileLog ./log/xs1Bridge-%Y-%m.log <name>
      + The following values ​​are recorded in logfile: Timer | xs1-status information +
    • +
=end html =begin html_DE @@ -784,102 +810,106 @@ sub is_in_array($$$)

xs1Bridge

    - Mit diesem Modul können Sie das Gerät xs1 der Firma EZcontrol auslesen. Das Modul ruft die Daten des xs1 via der Kommunikationsschnittstelle ab. Mit einem HTTP GET Requests erhält man die Antworten in Textform welche im Datenformat JSON (JavaScript Object Notation) ausgegeben werden. - Es werden Aktoren | Sensoren | Timer | Informationen vom xs1 ausgelesen und in Readings geschrieben. Bei jedem Auslesen werden nur Readings angelegt bzw. aktualisiert, welche auch im xs1 definiert und aktiv sind. Aktor | Sensor bzw. Timer Definitionen welche deaktiviert sind im xs1, werden NICHT ausgelesen. -

    + Mit diesem Modul können Sie das Gerät xs1 der Firma EZcontrol auslesen. Das Modul ruft die Daten des xs1 via der Kommunikationsschnittstelle ab. Mit einem HTTP GET Requests erhält man die Antworten in Textform welche im Datenformat JSON (JavaScript Object Notation) ausgegeben werden. + Es werden Aktoren | Sensoren | Timer | Informationen vom xs1 ausgelesen und in Readings geschrieben. Bei jedem Auslesen werden nur Readings angelegt bzw. aktualisiert, welche auch im xs1 definiert und aktiv sind. Aktor | Sensor bzw. Timer Definitionen welche deaktiviert sind im xs1, werden NICHT ausgelesen. +

    - Das Modul wurde entwickelt basierend auf dem Firmwarestand v4-Beta des xs1. Es kann aufgrund von unterschiedlichen Anpassungen innerhalb der Firmware des Herstellers zu Fehlern kommen.
    - Getestete Firmware: v4.0.0.5326 (Beta) @me | v3.0.0.4493 @ForumUser
    + Das Modul wurde entwickelt basierend auf dem Firmwarestand v4-Beta des xs1. Es kann aufgrund von unterschiedlichen Anpassungen innerhalb der Firmware des Herstellers zu Fehlern kommen.
    + Getestete Firmware: v4.0.0.5326 (Beta) @me | v3.0.0.4493 @ForumUser

    +
      + Derzeit implementierte Typen des xs1 zur Verarbeitung:
      -
        - Derzeit implementierte Typen des xs1 zur Verarbeitung:
        -
      • Aktor: dimmer, switch, shutter, timerswitch
      • -
      • Sensor: barometer, counter, counterdiff, light, motion, other, rain, rain_1h, rain_24h, rainintensity, remotecontrol, uv_index, waterdetector, winddirection, windgust, windspeed, windvariance
      • -


      - - - Define
      -
        - xs1 ohne Passwortabfrage:  define <NAME> xs1Bridge <IP>
        - xs1 mit Passwortabfrage:     define <NAME> xs1Bridge <User>:<Passwort>@<IP> -

        +
      • Aktor: dimmer, switch, shutter, timerswitch
      • +
      • Sensor: barometer, counter, counterdiff, light, motion, other, rain, rain_1h, rain_24h, rainintensity, remotecontrol, uv_index, waterdetector, winddirection, windgust, windspeed, windvariance
      • +


      - Ein anlegen des Modules ohne Angabe der IP vom xs1 ist nicht möglich. Sollte die IP bei der Moduldefinierung nicht erreichbar sein, so bricht der Define Vorgang ab. -
        -
      • <IP> ist IP-Adresse im lokalen Netzwerk
      • -
      • <User> ist der Administrator Benutzer admin (standard)
      • -
      • <Passwort> ist das vergebene Administrator Passwort im xs1.
      • -

      - Beispiele: -
        - define EZcontrol_xs1 xs1Bridge 192.168.1.45
        - define EZcontrol_xs1 xs1Bridge admin:geheim@192.168.1.45
        -
      -

    - Set -
      N/A

    - Get
    -
      N/A

    - - Attribute -
      -
    • debug (0,1,2)
      - Dies bringt das Modul in eine sehr ausführliche Debug-Ausgabe im Logfile. Somit lassen sich Programmteile kontrollieren und Fehler überprüfen.
      - (Default, debug 0) -

    • -
    • update_only_difference (0,1)
      - Die Aktoren welche im xs1 definiert wurden, werden nur bei Wertänderung aktualisiert.
      - (Default, update_only_difference 0)

    • -
    • view_Device_name (0,1)
      - Die Aktor Namen welche im xs1 definiert wurden, werden als Reading ausgelesen.
      - (Default, view_Device_name 0)
      -

    • -
    • view_Device_function (0,1)
      - Die Aktor Funktionen welche im xs1 definiert wurden, werden als Reading ausgelesen.
      - (Default, view_Device_function 0)
      -

    • -
    • xs1_blackl_aktor
      - Eine kommagetrennte Blacklist der Aktoren, welche nicht automatisch angelegt werden sollen sobald xs1_control = 1(aktiv).
      - (Beispiel: 2,40,3)
      -

    • -
    • xs1_blackl_sensor
      - Eine kommagetrennte Blacklist der Sensoren, welche nicht automatisch angelegt werden sollen sobald xs1_control = 1(aktiv).
      - (Beispiel: 3,37,55)
      -

    • -
    • xs1_control (0,1)
      - Die Freigabe zur Steuerung des xs1. Nach Aktivierung dieser, wird durch das xs1Dev Modul jeder Aktor und Sensor in FHEM angelegt.
      - (Default, xs1_control 0)
      -

    • -
    • xs1_interval (0,30,60,180,360)
      - Das ist der Intervall in Sekunden, in dem die Readings neu gelesen werden vom xs1.
      - Bei Aktoren werden nur unterschiedliche Zustände aktualisiert im eingestellten Intervall.
      - Sensoren werden unabhängig vom Zustand immer im Intervall aktualisiert.
      - (Default, xs1_interval 60) -


    • -
    - Erläuterung: -
      -
    • Auszug Readings:
    • -
        -
      • Aktor_(01-64)
      • definierter Aktor mit jeweiligem Zustand im Gerät
        -
      • Aktor_(01-64)_name
      • definierter Aktorname im Gerät
        -
      • Aktor_(01-64)_function(1-4)
      • definierte Aktorfunktion im Gerät
        -
      • Sensor_(01-64)
      • definierter Sensor im Gerät
        -
      • Sensor_(01-64)
      • definierter Sensorname im Gerät
        -
      • Timer_(01-128)
      • definierter Timer im Gerät
        -
      • xs1_bootloader
      • Firmwareversion des Bootloaders
        -
      • xs1_dhcp
      • DHCP an/aus
        -
      • xs1_features
      • erworbene Feature beim Kauf (A = SENDEN | B = EMPFANGEN | C = Skripte/Makros | D = Speicherkartenzugriff)
        -
      • xs1_firmware
      • Firmwareversion
        -
      • xs1_start
      • Gerätestart
        -

      -
    • Die Meldung "Error: Can't connect ..." oder "ERROR: empty answer received" im System-Logfile, besagt das kurzzeitig keine Abfrage erfolgen konnte.
      - (Das kann häufiger bei DLAN vorkommen.)

    • -
    • Sollte das Gerät nach 5 Verbindungsversuchen ebenfalls keine Verbindung erhalten haben, so schaltet das Modul auf < disable > !

    • -
    • Logfile Erstellung erfolgt automatisch nach dem definieren. | Schema: define FileLog_xs1Bridge FileLog ./log/xs1Bridge-%Y-%m.log <Name>
      - Folgende Werte werden im Logfile erfasst: Timer | xs1-Statusinformationen

    • -
    • Sollte das Gerät nach 5 Verbindungsversuchen ebenfalls keine Verbindung erhalten haben, so schaltet das Modul auf < disable > !

    • -
    + + Define
    +
      + xs1 ohne Passwortabfrage:  define <NAME> xs1Bridge <IP>
      + xs1 mit Passwortabfrage:     define <NAME> xs1Bridge <User>:<Passwort>@<IP> +

      + + Ein anlegen des Modules ohne Angabe der IP vom xs1 ist nicht möglich. Sollte die IP bei der Moduldefinierung nicht erreichbar sein, so bricht der Define Vorgang ab. +
        +
      • <IP> ist IP-Adresse im lokalen Netzwerk
      • +
      • <User> ist der Administrator Benutzer admin (standard)
      • +
      • <Passwort> ist das vergebene Administrator Passwort im xs1.
      • +

      + + Beispiele: +
        + define EZcontrol_xs1 xs1Bridge 192.168.1.45
        + define EZcontrol_xs1 xs1Bridge admin:geheim@192.168.1.45
        +
      +

    + + Set +
      N/A

    + + Get
    +
      N/A

    + + + Attribute +
      +
    • debug (0,1,2)
      + Dies bringt das Modul in eine sehr ausführliche Debug-Ausgabe im Logfile. Somit lassen sich Programmteile kontrollieren und Fehler überprüfen.
      + (Default, debug 0) +

    • +
    • update_only_difference (0,1)
      + Die Aktoren welche im xs1 definiert wurden, werden nur bei Wertänderung aktualisiert.
      + (Default, update_only_difference 0)

    • +
    • view_Device_name (0,1)
      + Die Aktor Namen welche im xs1 definiert wurden, werden als Reading ausgelesen.
      + (Default, view_Device_name 0)
      +

    • +
    • view_Device_function (0,1)
      + Die Aktor Funktionen welche im xs1 definiert wurden, werden als Reading ausgelesen.
      + (Default, view_Device_function 0)
      +

    • +
    • xs1_blackl_aktor
      + Eine kommagetrennte Blacklist der Aktoren, welche nicht automatisch angelegt werden sollen sobald xs1_control = 1(aktiv).
      + (Beispiel: 2,40,3)
      +

    • +
    • xs1_blackl_sensor
      + Eine kommagetrennte Blacklist der Sensoren, welche nicht automatisch angelegt werden sollen sobald xs1_control = 1(aktiv).
      + (Beispiel: 3,37,55)
      +

    • +
    • xs1_control (0,1)
      + Die Freigabe zur Steuerung des xs1. Nach Aktivierung dieser, wird durch das xs1Dev Modul jeder Aktor und Sensor in FHEM angelegt.
      + (Default, xs1_control 0)
      +

    • +
    • xs1_interval (0,30,60,180,360)
      + Das ist der Intervall in Sekunden, in dem die Readings neu gelesen werden vom xs1.
      + Bei Aktoren werden nur unterschiedliche Zustände aktualisiert im eingestellten Intervall.
      + Sensoren werden unabhängig vom Zustand immer im Intervall aktualisiert.
      + (Default, xs1_interval 60) +


    • +
    + Erläuterung: +
      +
    • Auszug Readings:
    • +
        +
      • Aktor_(01-64)
      • definierter Aktor mit jeweiligem Zustand im Gerät
        +
      • Aktor_(01-64)_name
      • definierter Aktorname im Gerät
        +
      • Aktor_(01-64)_function(1-4)
      • definierte Aktorfunktion im Gerät
        +
      • Sensor_(01-64)
      • definierter Sensor im Gerät
        +
      • Sensor_(01-64)
      • definierter Sensorname im Gerät
        +
      • Timer_(01-128)
      • definierter Timer im Gerät
        +
      • xs1_bootloader
      • Firmwareversion des Bootloaders
        +
      • xs1_dhcp
      • DHCP an/aus
        +
      • xs1_features
      • erworbene Feature beim Kauf (A = SENDEN | B = EMPFANGEN | C = Skripte/Makros | D = Speicherkartenzugriff)
        +
      • xs1_firmware
      • Firmwareversion
        +
      • xs1_start
      • Gerätestart
        +

      +
    • Die Meldung "Error: Can't connect ..." oder "ERROR: empty answer received" im System-Logfile, besagt das kurzzeitig keine Abfrage erfolgen konnte.
      + (Das kann häufiger bei DLAN vorkommen.)

    • +
    • Sollte das Gerät nach 5 Verbindungsversuchen ebenfalls keine Verbindung erhalten haben, so schaltet das Modul auf < disable > !

    • +
    • Logfile Erstellung erfolgt automatisch nach dem definieren. | Schema: define FileLog_xs1Bridge FileLog ./log/xs1Bridge-%Y-%m.log <Name>
      + Folgende Werte werden im Logfile erfasst: Timer | xs1-Statusinformationen

    • +
    • Sollte das Gerät nach 5 Verbindungsversuchen ebenfalls keine Verbindung erhalten haben, so schaltet das Modul auf < disable > !

    • +
=end html_DE =cut diff --git a/fhem/FHEM/88_xs1Dev.pm b/fhem/FHEM/88_xs1Dev.pm index 5ea4a9ffe..70f11219c 100644 --- a/fhem/FHEM/88_xs1Dev.pm +++ b/fhem/FHEM/88_xs1Dev.pm @@ -2,8 +2,8 @@ # $Id$ ################################################################# # logisches Modul - einzelnes Gerät, über das mit physikalisches -# Modul kommuniziert werden kann -# +# Modul kommuniziert werden kann +# # note / ToDo´s: # - PERL WARNING: Use of uninitialized value $_ # - PERL WARNING: Use of uninitialized value $cmdList in string @@ -13,429 +13,413 @@ package main; # Laden evtl. abhängiger Perl- bzw. FHEM-Module -use strict; -use warnings; # Warnings +use strict; +use warnings; # Warnings use POSIX; use Time::Local; -#use SetExtensions; -sub xs1Dev_Initialize($) { - my ($hash) = @_; +######################## +sub xs1Dev_Initialize { + my ($hash) = @_; - $hash->{Match} = "[x][s][1][D][e][v][_][A][k][t][o][r]_[0-6][0-9].*|[x][s][1][D][e][v][_][S][e][n][s][o][r]_[0-6][0-9].*"; ## zum testen - https://regex101.com/ - $hash->{DefFn} = "xs1Dev_Define"; - $hash->{AttrFn} = "xs1Dev_Attr"; - $hash->{ParseFn} = "xs1Dev_Parse"; - $hash->{SetFn} = "xs1Dev_Set"; - $hash->{UndefFn} = "xs1Dev_Undef"; - $hash->{AttrList} = "debug:0,1 ". - "IODev ". - "useSetExtensions:0,1 ". - $readingFnAttributes; + $hash->{Match} = '[x][s][1][D][e][v][#][A][k][t][o][r]#[0-6][0-9].*|[x][s][1][D][e][v][#][S][e][n][s][o][r]#[0-6][0-9].*'; ## zum testen - https://regex101.com/ + $hash->{DefFn} = 'xs1Dev_Define'; + $hash->{AttrFn} = 'xs1Dev_Attr'; + $hash->{ParseFn} = 'xs1Dev_Parse'; + $hash->{SetFn} = 'xs1Dev_Set'; + $hash->{UndefFn} = 'xs1Dev_Undef'; + $hash->{AttrList} = 'debug:0,1 '. + 'IODev '. + 'ignore:0,1 '. + 'useSetExtensions:0,1 '. + $readingFnAttributes; - $hash->{AutoCreate} = { "xs1Dev_Sensor_.*" => { GPLOT => "temp4hum4:Temp/Hum,", FILTER=>"%NAME", } }; - + $hash->{AutoCreate} = { 'xs1Dev_Sensor_.*' => { GPLOT => 'temp4hum4:Temp/Hum,', FILTER=>'%NAME', } }; + + return; } -sub xs1Dev_Define($$) { - # $def --> Definition des Module - # $hash --> ARRAY des Module - - my ($hash, $def) = @_; - my @arg = split("[ \t][ \t]*", $def); - #-----0------1------2----3----4 - return "Usage: define xs1Dev | wrong number of arguments" if( @arg != 4); - return "Usage: define xs1Dev | wrong ID, must be 1-64" if ( $arg[3] <1 || $arg[3] >64); - return "Usage: define xs1Dev | wrong Typ, must be A or S" if ( $arg[2] ne "A" && $arg[2] ne "S"); +######################## +sub xs1Dev_Define { + my ($hash, $def) = @_; + my @arg = split("[ \t][ \t]*", $def); - splice( @arg, 1, 1 ); - my $iodev; - my $i = 0; + #-----0------1------2----3----4 + return 'Usage: define xs1Dev | wrong number of arguments' if( @arg != 4); + return 'Usage: define xs1Dev | wrong ID, must be 1-64' if ( $arg[3] <1 || $arg[3] >64); + return 'Usage: define xs1Dev | wrong Typ, must be A or S' if ( $arg[2] ne 'A' && $arg[2] ne 'S'); - ############## !! ################ nicht genutzt derzeit ############# !! ################# - #### Schleife (Durchlauf der Argumente @arg) wo IODev= gefiltert wird aus define | Dispatch - foreach my $param ( @arg ) { - if( $param =~ m/IODev=([^\s]*)/ ) { - $iodev = $1; + splice( @arg, 1, 1 ); + my $iodev; + my $i = 0; + + ############## !! ################ nicht genutzt derzeit ############# !! ################# + #### Schleife (Durchlauf der Argumente @arg) wo IODev= gefiltert wird aus define | Dispatch + foreach my $param ( @arg ) { + if( $param =~ m/IODev=([^\s]*)/ ) { + $iodev = $1; splice( @arg, $i, 3 ); last; } $i++; - } - ########################################################################################### + } + ########################################################################################### - my $name = $hash->{NAME}; ## Der Definitionsname, mit dem das Gerät angelegt wurde. - my $typ = $hash->{TYPE}; ## Der Modulname, mit welchem die Definition angelegt wurde. - - #Log3 $name, 3, "$typ: Define arguments 0:$arg[0] | 1:$arg[1] | 2:$arg[2] | 3:$arg[3]"; + my $name = $hash->{NAME}; ## Der Definitionsname, mit dem das Gerät angelegt wurde. + my $typ = $hash->{TYPE}; ## Der Modulname, mit welchem die Definition angelegt wurde. - # Parameter Define - my $xs1_ID = $arg[2]; ## Zusatzparameter 1 bei Define - ggf. nur in Sub - my $xs1_typ1 = $arg[1]; ## A || S - - my $Device = $xs1_typ1.$xs1_ID; ## A02 || S05 - my $Device_count = 0; - my $Device_exist; - - ### Check A02 || S05 bereits definiert - foreach my $d (sort keys %defs) { - if(defined($defs{$d}) && defined($defs{$d}{ID}) && $defs{$d}{ID} eq $Device) { - $Device_count++; - $Device_exist = $d; - Log3 $name, 3, "$typ: $d $Device_count"; + #Log3 $name, 3, "$typ: Define arguments 0:$arg[0] | 1:$arg[1] | 2:$arg[2] | 3:$arg[3]"; + + # Parameter Define + my $xs1_ID = $arg[2]; ## Zusatzparameter 1 bei Define - ggf. nur in Sub + my $xs1_typ1 = $arg[1]; ## A || S + + my $Device = $xs1_typ1.$xs1_ID; ## A02 || S05 + my $Device_count = 0; + my $Device_exist; + + ### Check A02 || S05 bereits definiert + foreach my $d (sort keys %defs) { + if(defined($defs{$d}) && defined($defs{$d}{ID}) && $defs{$d}{ID} eq $Device) { + $Device_count++; + $Device_exist = $d; + Log3 $name, 3, "$typ: $d $Device_count"; } - } - - return "The xs1 $Device is already definded: $Device_exist" if ($Device_count != 0); + } - $hash->{ID} = $xs1_typ1.$xs1_ID; ## A02 || S05 - $modules{xs1Dev}{defptr}{$xs1_typ1.$xs1_ID} = $hash; ## !!! Adresse rückwärts dem Hash zuordnen (für ParseFn) + return "The xs1 $Device is already definded: $Device_exist" if ($Device_count != 0); - my $debug = AttrVal($hash->{NAME},"debug",0); - AttrVal($hash->{NAME},"useSetExtensions",0); - - $hash->{STATE} = "Defined"; ## Der Status des Modules nach Initialisierung. - $hash->{TIME} = time(); ## Zeitstempel, derzeit vom anlegen des Moduls - #$hash->{VERSION} = "1.17"; ## Version - - $hash->{xs1_name} = "undefined"; ## Aktor | Sensor Name welcher def. im xs1 - $hash->{xs1_typ} = "undefined"; ## xs1_Typ switch | hygrometer | temperature ... - - if ($xs1_typ1 eq "A"){ - $hash->{xs1_function1} = "undefined"; ## xs1_Funktion zugeordnete Funktion 1 - $hash->{xs1_function2} = "undefined"; ## xs1_Funktion zugeordnete Funktion 2 - $hash->{xs1_function3} = "undefined"; ## xs1_Funktion zugeordnete Funktion 3 - $hash->{xs1_function4} = "undefined"; ## xs1_Funktion zugeordnete Funktion 4 - } + $hash->{ID} = $xs1_typ1.$xs1_ID; ## A02 || S05 + $modules{xs1Dev}{defptr}{$xs1_typ1.$xs1_ID} = $hash; ## !!! Adresse rückwärts dem Hash zuordnen (für ParseFn) - # Attribut gesetzt - $attr{$name}{room} = "xs1" if( not defined( $attr{$name}{room} ) ); + my $debug = AttrVal($hash->{NAME},'debug',0); + AttrVal($hash->{NAME},'useSetExtensions',0); - AssignIoPort($hash,$iodev) if( !$hash->{IODev} ); ## sucht nach einem passenden IO-Gerät (physikalische Definition) + $hash->{STATE} = 'Defined'; ## Der Status des Modules nach Initialisierung. + $hash->{TIME} = time(); ## Zeitstempel, derzeit vom anlegen des Moduls - # alles mit IODev erst NACH AssignIoPort nutzbar !!! - $hash->{VERSION} = $hash->{IODev}->{VERSION}; ## Version - - if(defined($hash->{IODev}->{NAME})) { - Log3 $name, 4, "xs1Dev: $name - I/O Device is " . $hash->{IODev}->{NAME}; + $hash->{xs1_name} = 'undefined'; ## Aktor | Sensor Name welcher def. im xs1 + $hash->{xs1_typ} = 'undefined'; ## xs1_Typ switch | hygrometer | temperature ... + + if ($xs1_typ1 eq 'A'){ + $hash->{xs1_function1} = 'undefined'; ## xs1_Funktion zugeordnete Funktion 1 + $hash->{xs1_function2} = 'undefined'; ## xs1_Funktion zugeordnete Funktion 2 + $hash->{xs1_function3} = 'undefined'; ## xs1_Funktion zugeordnete Funktion 3 + $hash->{xs1_function4} = 'undefined'; ## xs1_Funktion zugeordnete Funktion 4 + } + + AssignIoPort($hash,$iodev) if( !$hash->{IODev} ); ## sucht nach einem passenden IO-Gerät (physikalische Definition) + + # alles mit IODev erst NACH AssignIoPort nutzbar !!! + $hash->{VERSION} = $hash->{IODev}->{VERSION}; ## Version + + if(defined($hash->{IODev}->{NAME})) { + Log3 $name, 4, "xs1Dev: $name - I/O Device is " . $hash->{IODev}->{NAME}; } else { - Log3 $name, 3, "xs1Dev: $name - no I/O Device, Please delete and restart FHEM."; + Log3 $name, 3, "xs1Dev: $name - no I/O Device, Please delete and restart FHEM."; } - #$iodev = $hash->{IODev}->{NAME}; - - # if(defined($hash->{IODev}->{xs1_ip})) { ## IP von xs1Bridge - Device aus HASH - # $hash->{xs1_ip} = $hash->{IODev}->{xs1_ip}; - # } - - return undef; + return; } -sub xs1Dev_Attr() -{ - my ($cmd,$name,$attrName,$attrValue) = @_; - my $hash = $defs{$name}; - my $typ = $hash->{TYPE}; - my $debug = AttrVal($hash->{NAME},"debug",0); - - #Debug " $name: Attr | Attributes $attrName = $attrValue" if($debug); + +######################## +sub xs1Dev_Attr { + my ($cmd,$name,$attrName,$attrValue) = @_; + my $hash = $defs{$name}; + my $typ = $hash->{TYPE}; + my $debug = AttrVal($hash->{NAME},'debug',0); + + #### Handling bei set ... attribute + if ($cmd eq 'set' && $init_done == 1 ) { + Log3 $name, 4, "$name: set attribute $attrName to $attrValue"; + } + + #### Handling bei del ... attribute + if ($cmd eq 'del') { + Log3 $name, 3, "$name: $cmd attribute $attrName"; + } + + return; } -sub xs1Dev_Set ($$@) -{ - my ( $hash, $name, @args ) = @_; - my $xs1_ID = $hash->{ID}; - my $typ = $hash->{TYPE}; ## xs1Dev - my $cmd = $args[0]; - - my $debug = AttrVal($hash->{NAME},"debug",0); - my $xs1_typ = $hash->{xs1_typ}; - my $Aktor_ID = substr($xs1_ID,1,2); ## A01 zu 01 - my $cmd2; ## notwendig für Switch Funktionsplatz xs1 - my $cmdFound; - - return "no set value specified" if(int(@args) < 1); - my %xs1_function = (); ## Funktionen in ARRAY schreiben - my %setList = (); ## Funktionen als Liste - my %setListPos = (); ## Funktionen als Position|Funktion - - Debug " -------------- ERROR CHECK - START --------------" if($debug && $cmd ne "?"); - # http://192.168.2.5/control?callback=cname&cmd=set_state_actuator&number=7&function=1 - - if (substr($xs1_ID,0,1) eq "A" && $xs1_typ ne "undefined") { ## nur bei Aktoren und nicht "undefined" - for (my $d = 0; $d < 4; $d++) { - if ($hash->{"xs1_function".($d+1)} ne "-") { - if ($hash->{"xs1_function".($d+1)} eq "dim_up") { ## FHEM Mod xs1 dim_up -> FHEM dimup - $xs1_function{"dimup:noArg"} = ($d+1); - } elsif ($hash->{"xs1_function".($d+1)} eq "dim_down") { ## FHEM Mod xs1 dim_down -> FHEM dimdown - $xs1_function{"dimdown:noArg"} = ($d+1); - } elsif (exists $xs1_function{$hash->{"xs1_function".($d+1)}.":noArg"}){ ## CHECK ob Funktion bereits exists - $xs1_function{$hash->{"xs1_function".($d+1)}."_".($d+1).":noArg"} = ($d+1); - } else { - $xs1_function{$hash->{"xs1_function".($d+1)}.":noArg"} = ($d+1); ## xs1 Standardbezeichnung Funktion - } - } - } - - if ($xs1_typ eq "dimmer"){ #bei dimmer Typ, dim hinzufügen FHEM - $xs1_function{"dim"} = (5); - } - - while ( (my $k,my $v) = each %xs1_function ) { - if ($v > 0 && $v < 7) { - $setListPos{$v."|".$k} = $k; - #Debug " $name: Set | $k|$v" if($debug && $cmd ne "?"); - } - } - - my $setList = join(" ", keys %xs1_function); - my $setListAll = join(" ", keys %setListPos); - - my $cmdFound = index($setListAll, $cmd.":"); ## check cmd in setListAll - Zuordnung Platz - my $cmdFound2 = ""; - - if ($cmdFound >= 0) { #$cmd für Sendebefehl anpassen - $cmdFound2 = substr($setListAll,$cmdFound-2,1); - $cmd2 = "function=".$cmdFound2; - } else { - $cmd2 = $cmd.$args[1] if (defined $args[1]); - } - - ### dimmer - spezifisch dim hinzufügen FHEM + value Check - if ($xs1_typ eq "dimmer" && $cmd eq "dim") { - if (not defined $args[1]) { - return "dim value arguments failed"; - } elsif ($args[1] !~ /[a-zA-Z]/ && $args[1] <= 1 || $args[1] !~ /[a-zA-Z]/ && $args[1] >= 99) { - return "dim value must be 1 to 99"; - } elsif ($args[1] =~ /[a-zA-Z]/) { - return "wrong dim value format! only value from 1 to 99"; - } else { - $cmd = $cmd.$args[1]."%"; ## FHEM state mod --> anstatt nur dim --> dim47% - } - } - Debug " $name: Set | xs1_typ=$xs1_typ cmd=$cmd setListAll=$setListAll cmdFound=$cmdFound cmdFound2=$cmdFound2" if($debug && $cmd ne "?"); - - if(AttrVal($name,"useSetExtensions",undef) || AttrVal($name,"useSetExtensions","0")) { - $cmd =~ s/([.?*])/\\$1/g; - if($setList !~ m/\b$cmd\b/) { - Debug " $name: Set | useSetExtensions check" if($debug && $cmd ne "?"); - unshift @args, $name; - return SetExtensions($hash, $setList, $name, @args); - } - SetExtensionsCancel($hash); - } else { - return "Unknown argument ?, choose one of $setList" if($args[0] eq "?"); - } +######################## +sub xs1Dev_Set { + my ( $hash, $name, @args ) = @_; + my $xs1_ID = $hash->{ID}; + my $typ = $hash->{TYPE}; ## xs1Dev + my $cmd = $args[0]; - #Debug " $name: Set | xs1_typ=$xs1_typ (after mod) cmd=$cmd" if($debug && $cmd ne "?"); + my $debug = AttrVal($hash->{NAME},'debug',0); + my $xs1_typ = $hash->{xs1_typ}; + my $Aktor_ID = substr($xs1_ID,1,2); ## A01 zu 01 + my $cmd2; ## notwendig für Switch Funktionsplatz xs1 + my $cmdFound; - if(defined($hash->{IODev}->{NAME})) { - if ($xs1_typ eq "switch" || $xs1_typ eq "dimmer" || $xs1_typ eq "shutter" || $xs1_typ eq "timerswitch" && $cmd ne "?") { - Debug " $name: Set IOWrite | xs1_ID=$xs1_ID xs1_typ=$xs1_typ cmd=$cmd cmd2=$cmd2" if($debug && $xs1_typ ne "temperature" && $xs1_typ ne "hygrometer"); - #Log3 $name, 3, "$name: Set IOWrite | xs1_ID=$xs1_ID xs1_typ=$xs1_typ cmd=$cmd cmd2=$cmd2 IODev=$hash->{IODev}->{NAME}"; - Log3 $name, 3, "$typ set $name $cmd"; - - IOWrite($hash, $xs1_ID, $xs1_typ, $cmd, $cmd2); - readingsSingleUpdate($hash, "state", $cmd , 1); - } - #else { - #Log3 $name, 2, "$name: Device NOT SUPPORTED for Dispatch. In xs1 disabled."; - #} - } else { - return "no IODev define. Please define xs1Bridge."; - } + return 'no set value specified' if(int(@args) < 1); + my %setList = (); ## Funktionen als Liste + my %setListPos = (); ## Funktionen als Position|Funktion + my %xs1_function = (); ## Funktionen in ARRAY schreiben - #Debug " $name: Set | xs1_ID=$xs1_ID xs1_typ=$xs1_typ" if($debug); - Debug " -------------- ERROR CHECK - END --------------" if($debug); - } + Debug ' -------------- ERROR CHECK - START --------------' if($debug && $cmd ne '?'); + # http://192.168.2.5/control?callback=cname&cmd=set_state_actuator&number=7&function=1 - return undef; -} - -sub xs1Dev_Parse($$) ## Input Data from 88_xs1Bridge -{ - my ( $io_hash, $data) = @_; ## $io_hash = ezControl -> def. Name von xs1Bridge + if (substr($xs1_ID,0,1) eq 'A' && $xs1_typ ne 'undefined') { ## nur bei Aktoren und nicht 'undefined' + for (my $d = 0; $d < 4; $d++) { + if ($hash->{'xs1_function'.($d+1)} ne '-') { + if ($hash->{'xs1_function'.($d+1)} eq 'dim_up') { ## FHEM Mod xs1 dim_up -> FHEM dimup + $xs1_function{'dimup:noArg'} = ($d+1); + } elsif ($hash->{'xs1_function'.($d+1)} eq 'dim_down') { ## FHEM Mod xs1 dim_down -> FHEM dimdown + $xs1_function{'dimdown:noArg'} = ($d+1); + } elsif (exists $xs1_function{$hash->{'xs1_function'.($d+1)}.':noArg'}){ ## CHECK ob Funktion bereits exists + $xs1_function{$hash->{'xs1_function'.($d+1)}.'_'.($d+1).':noArg'} = ($d+1); + } else { + $xs1_function{$hash->{'xs1_function'.($d+1)}.':noArg'} = ($d+1); ## xs1 Standardbezeichnung Funktion + } + } + } - my ($xs1Dev,$xs1_readingsname,$xs1_ID,$xs1_typ2,$xs1_value,$xs1_f1,$xs1_f2,$xs1_f3,$xs1_f4,$xs1_name) = split("#", $data); - my $xs1_typ1 = substr($xs1_readingsname,0,1); ## A || S + if ($xs1_typ eq 'dimmer'){ #bei dimmer Typ, dim hinzufügen FHEM + $xs1_function{'dim'} = (5); + } - my $def = $modules{xs1Dev}{defptr}{$xs1_typ1.$xs1_ID}; - $def = $modules{xs1Dev}{defptr}{$xs1_typ1.$xs1_ID} if(!$def); ## {xs1Dev}{defptr}{A02} - - my $hash = $def; - $hash->{xs1_typ} = $xs1_typ2; - $hash->{xs1_name} = $xs1_name; - my $IODev = $io_hash->{NAME}; + while ( (my $k,my $v) = each %xs1_function ) { + if ($v > 0 && $v < 7) { + $setListPos{$v.'|'.$k} = $k; + #Debug " $name: Set | $k|$v" if($debug && $cmd ne "?"); + } + } - my $name = $hash->{NAME}; ## xs1Dev_Aktor_01 - my $typ = $hash->{TYPE}; ## xs1Dev - $typ = "xs1Dev" if (!$def); ## Erstanlegung - - ###### Define and values update ###### - #Log3 $typ, 3, "$typ: Parse | Data: $xs1Dev | $xs1_readingsname | $xs1_ID | $xs1_typ2 | $xs1_value | $xs1_typ1" if (!$def); - #Log3 $typ, 3, "$typ: Parse | Data: $xs1Dev | $xs1_readingsname | $xs1_ID | $xs1_typ2 | $xs1_value | $xs1_typ1"; + my $setList = join(' ', keys %xs1_function); + my $setListAll = join(' ', keys %setListPos); - if(!$def) { - # "UNDEFINED xs1Dev_Aktor_12 xs1Dev A 12" - Log3 $name, 3, "$typ: Unknown device ".$xs1Dev."_".$xs1_readingsname."_"."$xs1_ID $xs1_ID $xs1_typ1 , please define it"; - return "UNDEFINED xs1Dev"."_".$xs1_readingsname."_"."$xs1_ID xs1Dev $xs1_typ1 $xs1_ID"; - } else { - #Log3 $name, 3, "$typ: device $xs1_readingsname"."_"."$xs1_ID xs1_value:$xs1_value xs1_typ2:$xs1_typ2"; - - AssignIoPort($hash, $io_hash); ## sucht nach einem passenden IO-Gerät (physikalische Definition) - - if ($xs1_readingsname eq "Aktor") { ## zugeordnete xs1_Funktionen - $hash->{xs1_function1} = $xs1_f1; - $hash->{xs1_function2} = $xs1_f2; - $hash->{xs1_function3} = $xs1_f3; - $hash->{xs1_function4} = $xs1_f4; - } - - #### Typ switch | on | off mod for FHEM Default - if ($xs1_typ2 eq "switch") { - if ($xs1_value == 0) { $xs1_value = "off"; } - elsif ($xs1_value == 100) { $xs1_value = "on"; } - readingsSingleUpdate($hash, "state", $xs1_value ,1); # Aktor | Sensor Update value - - ## RegEx devStateIcon da Symbole nicht gleich benannt -> dim_up | dim_down - if ($hash->{xs1_function1} eq "dim_up" || $hash->{xs1_function2} eq "dim_up" || $hash->{xs1_function3} eq "dim_up" || $hash->{xs1_function4} eq "dim_up" || - $hash->{xs1_function1} eq "dim_down" || $hash->{xs1_function2} eq "dim_down" || $hash->{xs1_function3} eq "dim_down" || $hash->{xs1_function4} eq "dim_down" ) { - $attr{$name}{devStateIcon} = "dim_up:dimup dim_down:dimdown" if( not defined( $attr{$name}{devStateIcon} ) ); - } - - } - #### Typ temperature - elsif ($xs1_typ2 eq "temperature") { - my $xs1_value_new = "T: ".$xs1_value; ## temperature mod for FHEM Default + my $cmdFound = index($setListAll, $cmd.':'); ## check cmd in setListAll - Zuordnung Platz + my $cmdFound2 = ''; - readingsBeginUpdate($hash); - readingsBulkUpdate($hash, "state", $xs1_value_new); - readingsBulkUpdate($hash, "temperature", $xs1_value); - readingsEndUpdate($hash, 1); - } - #### Typ hygrometer - elsif ($xs1_typ2 eq "hygrometer") { - my $xs1_value_new = "H: ".$xs1_value; ## hygrometer mod for FHEM Default + if ($cmdFound >= 0) { #$cmd für Sendebefehl anpassen + $cmdFound2 = substr($setListAll,$cmdFound-2,1); + $cmd2 = 'function='.$cmdFound2; + } else { + $cmd2 = $cmd.$args[1] if (defined $args[1]); + } - readingsBeginUpdate($hash); - readingsBulkUpdate($hash, "state", $xs1_value_new); - readingsBulkUpdate($hash, "humidity", $xs1_value); - readingsEndUpdate($hash, 1); - } - #### Typ dimmer - elsif ($xs1_typ2 eq "dimmer") { - - ## RegEx devStateIcon da Symbole nicht durchweg von 0 - 100 | dim_up | dim_down - $attr{$name}{devStateIcon} = "dim0[1-6]\\D%:dim06% dim[7-9]\\D|dim[1][0-2]%:dim12% dim[1][3-8]%:dim18% \n" - ."dim[1][9]|dim[2][0-5]%:dim25% dim[2][6-9]|dim[3][0-1]%:dim31% dim[3][2-7]%:dim37% \n" - ."dim[3][8-9]|dim[4][0-3]%:dim43% dim[4][4-9]|dim[5][0]%:dim50% dim[5][1-6]%:dim56% \n" - ."dim[5][7-9]|dim[6][0-2]%:dim62% dim[6][3-8]%:dim68% dim[6][9]|dim[7][0-5]%:dim75% \n" - ."dim[7][6-9]|dim[8][0-1]%:dim81% dim[8][2-7]%:dim87% dim[8][8-9]|dim[9][0-3]%:dim93% \n" - ."dim[9][4-9]|dim[1][0][0]%:dim100% dim[_][u][p]:dimup dim[_][d][o]:dimdown" if( not defined( $attr{$name}{devStateIcon} ) ); + ### dimmer - spezifisch dim hinzufügen FHEM + value Check + if ($xs1_typ eq 'dimmer' && $cmd eq 'dim') { + if (not defined $args[1]) { + return 'dim value arguments failed'; + } elsif ($args[1] !~ /[a-zA-Z]/ && $args[1] <= 1 || $args[1] !~ /[a-zA-Z]/ && $args[1] >= 99) { + return 'dim value must be 1 to 99'; + } elsif ($args[1] =~ /[a-zA-Z]/) { + return 'wrong dim value format! only value from 1 to 99'; + } else { + $cmd = $cmd.$args[1].'%'; ## FHEM state mod --> anstatt nur dim --> dim47% + } + } - if ($xs1_value ne "0.0") { - $xs1_value = "dim".sprintf("%02d", $xs1_value)."%"; - } elsif ($xs1_value eq "0.0") { - $xs1_value = "off"; - } - - readingsSingleUpdate($hash, "state", $xs1_value ,1); - } - #### Typ shutter | on | off mod for FHEM Default - elsif ($xs1_typ2 eq "shutter") { - if ($xs1_value == 0) { $xs1_value = "off"; } - elsif ($xs1_value == 100) { $xs1_value = "on"; } - readingsSingleUpdate($hash, "state", $xs1_value ,1); - } - #### Typ timerswitch | on | off mod for FHEM Default - elsif ($xs1_typ2 eq "timerswitch") { - if ($xs1_value == 0) { $xs1_value = "off"; } - elsif ($xs1_value == 100) { $xs1_value = "on"; } - readingsSingleUpdate($hash, "state", $xs1_value ,1); - } - elsif ($xs1_typ2 eq "barometer") { - readingsBeginUpdate($hash); - readingsSingleUpdate($hash, "pressure", $xs1_value ,1); - readingsSingleUpdate($hash, "state", "P: ".$xs1_value ,1); - readingsEndUpdate($hash, 1); - } - elsif ($xs1_typ2 eq "rain") { - readingsBeginUpdate($hash); - readingsSingleUpdate($hash, "rain", $xs1_value ,1); - readingsSingleUpdate($hash, "state", "R: ".$xs1_value ,1); - readingsEndUpdate($hash, 1); - } - elsif ($xs1_typ2 eq "rain_1h") { - readingsBeginUpdate($hash); - readingsSingleUpdate($hash, "rain_calc_h", $xs1_value ,1); - readingsSingleUpdate($hash, "state", "R: ".$xs1_value ,1); - readingsEndUpdate($hash, 1); - } - elsif ($xs1_typ2 eq "rain_24h") { - readingsBeginUpdate($hash); - readingsSingleUpdate($hash, "rain_calc_d", $xs1_value ,1); - readingsSingleUpdate($hash, "state", "R: ".$xs1_value ,1); - readingsEndUpdate($hash, 1); - } - elsif ($xs1_typ2 eq "winddirection") { - readingsBeginUpdate($hash); - readingsSingleUpdate($hash, "Winddirection", $xs1_value ,1); - readingsSingleUpdate($hash, "state", "D: ".$xs1_value ,1); - readingsEndUpdate($hash, 1); - } - elsif ($xs1_typ2 eq "windspeed") { - readingsBeginUpdate($hash); - readingsSingleUpdate($hash, "Windspeed", $xs1_value ,1); - readingsSingleUpdate($hash, "state", "W: ".$xs1_value ,1); - readingsEndUpdate($hash, 1); - } - elsif ($xs1_typ2 eq "counter" || $xs1_typ2 eq "counterdiff" || $xs1_typ2 eq "fencedetector" || $xs1_typ2 eq "gas_consump" || $xs1_typ2 eq "gas_peak" || - $xs1_typ2 eq "light" || $xs1_typ2 eq "motion" || $xs1_typ2 eq "other" || $xs1_typ2 eq "rainintensity" || $xs1_typ2 eq "remotecontrol" || - $xs1_typ2 eq "uv_index" || $xs1_typ2 eq "waterdetector" || $xs1_typ2 eq "waterlevel" || $xs1_typ2 eq "windgust" || $xs1_typ2 eq "windvariance" || - $xs1_typ2 eq "wtr_consump" || $xs1_typ2 eq "wtr_peak") { - readingsSingleUpdate($hash, "state", $xs1_value ,1); - } - ### Fenstermelder = windowopen | Tuermelder = dooropen --> 0 zu / 100 offen | mod for FHEM Default - elsif ($xs1_typ2 eq "dooropen" || $xs1_typ2 eq "windowopen") { - if ($xs1_value == 0.0) { $xs1_value = "closed";} elsif ($xs1_value == 100.0) { $xs1_value = "Open"; } - readingsBeginUpdate($hash); - if ($xs1_typ2 eq "windowopen") { - readingsSingleUpdate($hash, "Window", $xs1_value ,1); - } - if ($xs1_typ2 eq "dooropen") { - readingsSingleUpdate($hash, "Door", $xs1_value ,1); - } - my $value = Value($name); - my $OldValue = OldValue($name); - if ($value ne $OldValue) { - readingsSingleUpdate($hash, "Previous", $xs1_value ,0); - } - readingsSingleUpdate($hash, "state", $xs1_value ,0); - readingsEndUpdate($hash, 1); - ### alles andere ... - } else { - readingsBeginUpdate($hash); - readingsSingleUpdate($hash, "state", $xs1_value ,0); - readingsEndUpdate($hash, 1); - } - } - - return $name; + Debug " $name: Set | xs1_typ=$xs1_typ cmd=$cmd setListAll=$setListAll cmdFound=$cmdFound cmdFound2=$cmdFound2" if($debug && $cmd ne '?'); + + if(AttrVal($name,'useSetExtensions',undef) || AttrVal($name,'useSetExtensions',0)) { + $cmd =~ s/([.?*])/\\$1/g; + if ($setList !~ m/\b$cmd\b/) { + Debug " $name: Set | useSetExtensions check" if($debug && $cmd ne '?'); + unshift @args, $name; + return SetExtensions($hash, $setList, $name, @args); + } + SetExtensionsCancel($hash); + } else { + return "Unknown argument ?, choose one of $setList" if($args[0] eq '?'); + } + + #Debug " $name: Set | xs1_typ=$xs1_typ (after mod) cmd=$cmd" if($debug && $cmd ne "?"); + + if (defined($hash->{IODev}->{NAME})) { + if ($xs1_typ eq 'switch' || $xs1_typ eq 'dimmer' || $xs1_typ eq 'shutter' || $xs1_typ eq 'timerswitch' && $cmd ne '?') { + Debug " $name: Set IOWrite | xs1_ID=$xs1_ID xs1_typ=$xs1_typ cmd=$cmd cmd2=$cmd2" if($debug && $xs1_typ ne 'temperature' && $xs1_typ ne 'hygrometer'); + #Log3 $name, 3, "$name: Set IOWrite | xs1_ID=$xs1_ID xs1_typ=$xs1_typ cmd=$cmd cmd2=$cmd2 IODev=$hash->{IODev}->{NAME}"; + Log3 $name, 3, "$typ set $name $cmd"; + + IOWrite($hash, $xs1_ID, $xs1_typ, $cmd, $cmd2); + readingsSingleUpdate($hash, 'state', $cmd , 1); + } + #else { + #Log3 $name, 2, "$name: Device NOT SUPPORTED for Dispatch. In xs1 disabled."; + #} + } else { + return 'no IODev define. Please define xs1Bridge.'; + } + + #Debug " $name: Set | xs1_ID=$xs1_ID xs1_typ=$xs1_typ" if($debug); + Debug ' -------------- ERROR CHECK - END --------------' if($debug); + } + + return; } -sub xs1Dev_Undef($$) -{ - my ( $hash, $name) = @_; - my $typ = $hash->{TYPE}; - - delete($modules{xs1Dev}{defptr}{$hash->{ID}}); - Log3 $name, 3, "$typ: Device with Name $name delete"; - return undef; + +######################## +sub xs1Dev_Parse { ## Input Data from 88_xs1Bridge + my ( $io_hash, $data) = @_; ## $io_hash = ezControl -> def. Name von xs1Bridge + + my ($xs1Dev,$xs1_readingsname,$xs1_ID,$xs1_typ2,$xs1_value,$xs1_f1,$xs1_f2,$xs1_f3,$xs1_f4,$xs1_name) = split('#', $data); + my $xs1_typ1 = substr($xs1_readingsname,0,1); ## A || S + + my $def = $modules{xs1Dev}{defptr}{$xs1_typ1.$xs1_ID}; + $def = $modules{xs1Dev}{defptr}{$xs1_typ1.$xs1_ID} if(!$def); ## {xs1Dev}{defptr}{A02} + + my $hash = $def; + $hash->{xs1_typ} = $xs1_typ2; + $hash->{xs1_name} = $xs1_name; + my $IODev = $io_hash->{NAME}; + + my $name = $hash->{NAME}; ## xs1Dev_Aktor_01 + my $typ = $hash->{TYPE}; ## xs1Dev + $typ = 'xs1Dev' if (!$def); ## Erstanlegung + + ###### Define and values update ###### + #Log3 $typ, 3, "$typ: Parse | Data: $xs1Dev | $xs1_readingsname | $xs1_ID | $xs1_typ2 | $xs1_value | $xs1_typ1" if (!$def); + #Log3 $typ, 3, "$typ: Parse | Data: $xs1Dev | $xs1_readingsname | $xs1_ID | $xs1_typ2 | $xs1_value | $xs1_typ1"; + + if(!$def) { + Log3 $name, 3, "$typ: Unknown device ".$xs1Dev.'_'.$xs1_readingsname.'_'."$xs1_ID $xs1_ID $xs1_typ1 , please define it"; + return 'UNDEFINED xs1Dev'.'_'.$xs1_readingsname.'_'."$xs1_ID xs1Dev $xs1_typ1 $xs1_ID"; ## "UNDEFINED xs1Dev_Aktor_12 xs1Dev A 12" + } else { + #Log3 $name, 3, "$typ: device $xs1_readingsname".'_'."$xs1_ID xs1_value:$xs1_value xs1_typ2:$xs1_typ2"; + + AssignIoPort($hash, $io_hash); ## sucht nach einem passenden IO-Gerät (physikalische Definition) + + if ($xs1_readingsname eq 'Aktor') { ## zugeordnete xs1_Funktionen + $hash->{xs1_function1} = $xs1_f1; + $hash->{xs1_function2} = $xs1_f2; + $hash->{xs1_function3} = $xs1_f3; + $hash->{xs1_function4} = $xs1_f4; + } + + readingsBeginUpdate($hash); + + #### Typ switch | on | off mod for FHEM Default + if ($xs1_typ2 eq 'switch') { + if ($xs1_value == 0) { $xs1_value = 'off'; } + elsif ($xs1_value == 100) { $xs1_value = 'on'; } + readingsBulkUpdate($hash, 'state', $xs1_value); # Aktor | Sensor Update value + + ## RegEx devStateIcon da Symbole nicht gleich benannt -> dim_up | dim_down + if ($hash->{xs1_function1} eq 'dim_up' || $hash->{xs1_function2} eq 'dim_up' || $hash->{xs1_function3} eq 'dim_up' || $hash->{xs1_function4} eq 'dim_up' || + $hash->{xs1_function1} eq 'dim_down' || $hash->{xs1_function2} eq 'dim_down' || $hash->{xs1_function3} eq 'dim_down' || $hash->{xs1_function4} eq 'dim_down' ) { + $attr{$name}{devStateIcon} = 'dim_up:dimup dim_down:dimdown' if( not defined( $attr{$name}{devStateIcon} ) ); + } + + #### Typ temperature + } elsif ($xs1_typ2 eq 'temperature') { + my $xs1_value_new = 'T: '.$xs1_value; ## temperature mod for FHEM Default + + readingsBulkUpdate($hash, 'state', $xs1_value_new); + readingsBulkUpdate($hash, 'temperature', $xs1_value); + + #### Typ hygrometer + } elsif ($xs1_typ2 eq 'hygrometer') { + my $xs1_value_new = 'H: '.$xs1_value; ## hygrometer mod for FHEM Default + + readingsBulkUpdate($hash, 'state', $xs1_value_new); + readingsBulkUpdate($hash, 'humidity', $xs1_value); + + #### Typ dimmer + } elsif ($xs1_typ2 eq 'dimmer') { + ## RegEx devStateIcon da Symbole nicht durchweg von 0 - 100 | dim_up | dim_down + $attr{$name}{devStateIcon} = "dim0[1-6]\\D%:dim06% dim[7-9]\\D|dim[1][0-2]%:dim12% dim[1][3-8]%:dim18% \n" + ."dim[1][9]|dim[2][0-5]%:dim25% dim[2][6-9]|dim[3][0-1]%:dim31% dim[3][2-7]%:dim37% \n" + ."dim[3][8-9]|dim[4][0-3]%:dim43% dim[4][4-9]|dim[5][0]%:dim50% dim[5][1-6]%:dim56% \n" + ."dim[5][7-9]|dim[6][0-2]%:dim62% dim[6][3-8]%:dim68% dim[6][9]|dim[7][0-5]%:dim75% \n" + ."dim[7][6-9]|dim[8][0-1]%:dim81% dim[8][2-7]%:dim87% dim[8][8-9]|dim[9][0-3]%:dim93% \n" + ."dim[9][4-9]|dim[1][0][0]%:dim100% dim[_][u][p]:dimup dim[_][d][o]:dimdown" if( not defined( $attr{$name}{devStateIcon} ) ); + + if ($xs1_value ne '0.0') { + $xs1_value = 'dim'.sprintf("%02d", $xs1_value).'%'; + } elsif ($xs1_value eq '0.0') { + $xs1_value = 'off'; + } + + readingsBulkUpdate($hash, 'state', $xs1_value); + + #### Typ shutter | on | off mod for FHEM Default + } elsif ($xs1_typ2 eq 'shutter') { + if ($xs1_value == 0) { $xs1_value = 'off'; } + elsif ($xs1_value == 100) { $xs1_value = 'on'; } + readingsBulkUpdate($hash, 'state', $xs1_value); + + #### Typ timerswitch | on | off mod for FHEM Default + } elsif ($xs1_typ2 eq 'timerswitch') { + if ($xs1_value == 0) { $xs1_value = 'off'; } + elsif ($xs1_value == 100) { $xs1_value = 'on'; } + readingsBulkUpdate($hash, 'state', $xs1_value); + + } elsif ($xs1_typ2 eq 'barometer') { + readingsBulkUpdate($hash, 'pressure', $xs1_value); + readingsBulkUpdate($hash, 'state', 'P: '.$xs1_value); + + } elsif ($xs1_typ2 eq 'rain') { + readingsBulkUpdate($hash, 'rain', $xs1_value); + readingsBulkUpdate($hash, 'state', 'R: '.$xs1_value); + + } elsif ($xs1_typ2 eq 'rain_1h') { + readingsBulkUpdate($hash, 'rain_calc_h', $xs1_value); + readingsBulkUpdate($hash, 'state', 'R: '.$xs1_value); + + } elsif ($xs1_typ2 eq 'rain_24h') { + readingsBulkUpdate($hash, 'rain_calc_d', $xs1_value); + readingsBulkUpdate($hash, 'state', 'R: '.$xs1_value); + + } elsif ($xs1_typ2 eq 'winddirection') { + readingsBulkUpdate($hash, 'Winddirection', $xs1_value); + readingsBulkUpdate($hash, 'state', 'D: '.$xs1_value); + + } elsif ($xs1_typ2 eq 'windspeed') { + readingsBulkUpdate($hash, 'Windspeed', $xs1_value); + readingsBulkUpdate($hash, 'state', 'W: '.$xs1_value); + + } elsif ($xs1_typ2 eq 'counter' || $xs1_typ2 eq 'counterdiff' || $xs1_typ2 eq 'fencedetector' || $xs1_typ2 eq 'gas_consump' || $xs1_typ2 eq 'gas_peak' || + $xs1_typ2 eq 'light' || $xs1_typ2 eq 'motion' || $xs1_typ2 eq 'other' || $xs1_typ2 eq 'rainintensity' || $xs1_typ2 eq 'remotecontrol' || + $xs1_typ2 eq 'uv_index' || $xs1_typ2 eq 'waterdetector' || $xs1_typ2 eq 'waterlevel' || $xs1_typ2 eq 'windgust' || $xs1_typ2 eq 'windvariance' || + $xs1_typ2 eq 'wtr_consump' || $xs1_typ2 eq 'wtr_peak') { + readingsBulkUpdate($hash, 'state', $xs1_value); + + ### Fenstermelder = windowopen | Tuermelder = dooropen --> 0 zu / 100 offen | mod for FHEM Default + } elsif ($xs1_typ2 eq 'dooropen' || $xs1_typ2 eq 'windowopen') { + if ($xs1_value == 0.0) { $xs1_value = 'closed';} elsif ($xs1_value == 100.0) { $xs1_value = 'Open'; } + + if ($xs1_typ2 eq 'windowopen') { readingsBulkUpdate($hash, 'Window', $xs1_value); } + if ($xs1_typ2 eq 'dooropen') { readingsBulkUpdate($hash, 'Door', $xs1_value); } + + my $value = Value($name); + my $OldValue = OldValue($name); + if ($value ne $OldValue) { readingsBulkUpdate($hash, 'Previous', $xs1_value ,0); } + readingsBulkUpdate($hash, 'state', $xs1_value); + ### alles andere ... + } else { + readingsBulkUpdate($hash, 'state', $xs1_value); + } + + readingsEndUpdate($hash, 1); + } + + return $name; } + +######################## +sub xs1Dev_Undef { + my ( $hash, $name) = @_; + my $typ = $hash->{TYPE}; + + delete($modules{xs1Dev}{defptr}{$hash->{ID}}); + Log3 $name, 3, "$typ: Device with Name $name delete"; + return; +} + + +######################## # Eval-Rückgabewert für erfolgreiches # Laden des Moduls 1; @@ -451,39 +435,39 @@ sub xs1Dev_Undef($$)

xs1Dev

    - This module works with the xs1Bridge module. (The xs1_control attribute in the xs1Bridge module must be set to 1!)
    - It communicates with this and creates all actuators of the xs1 as a device in FHEM. So you can control the actuators of the xs1 from the FHEM.

    - The module was developed based on the firmware version v4-Beta of the xs1. There may be errors due to different adjustments within the manufacturer's firmware. -
    - -
      - Currently implemented types of xs1 for processing:
      -
    • Aktor: dimmer, switch, shutter, timerswitch
    • -
    • Sensor: barometer, counter, counterdiff, light, motion, other, rain, rain_1h, rain_24h, rainintensity, remotecontrol, uv_index, waterdetector, winddirection, windgust, windspeed, windvariance
    • -


    + This module works with the xs1Bridge module. (The xs1_control attribute in the xs1Bridge module must be set to 1!)
    + It communicates with this and creates all actuators of the xs1 as a device in FHEM. So you can control the actuators of the xs1 from the FHEM.

    + The module was developed based on the firmware version v4-Beta of the xs1. There may be errors due to different adjustments within the manufacturer's firmware. +
    + +
      + Currently implemented types of xs1 for processing:
      +
    • Aktor: dimmer, switch, shutter, timerswitch
    • +
    • Sensor: barometer, counter, counterdiff, light, motion, other, rain, rain_1h, rain_24h, rainintensity, remotecontrol, uv_index, waterdetector, winddirection, windgust, windspeed, windvariance
    • +


    - - Define
    -
      - define <name> xs1Dev <Typ> <ID> IODev=<NAME> -

      + + Define
      +
        + define <name> xs1Dev <Typ> <ID> IODev=<NAME> +

        - It is not possible to create the module without specifying type and ID of xs1. -
          -
        • <ID> is internal id in xs1.
        • -
        -
          -
        • <Typ> is the abbreviation A for actuators or S for sensors.
        • -

        - example: -
          - define xs1Dev_Aktor_02 xs1Dev A 02 IODev=ezControl -
        -

      - Set -
        set <name> <value>

      - in which value one of the following values:
      -
        + It is not possible to create the module without specifying type and ID of xs1. +
          +
        • <ID> is internal id in xs1.
        • +
        +
          +
        • <Typ> is the abbreviation A for actuators or S for sensors.
        • +

        + example: +
          + define xs1Dev_Aktor_02 xs1Dev A 02 IODev=ezControl +
        +

      + Set +
        set <name> <value>

      + in which value one of the following values:
      +
        on
        off
        dimup
        @@ -492,36 +476,36 @@ sub xs1Dev_Undef($$) on, wait, off
        absolut
        wait
        - long on
        - long off
        - Stopp
        - on, wait, on
        - off, wait, off
        - impuls
        + long on
        + long off
        + Stopp
        + on, wait, on
        + off, wait, off
        + impuls

      - Get
      -
        N/A

      - - Attributes -
        -
      • debug (0,1)
        - This brings the module into a very detailed debug output in the logfile. Thus, program parts can be controlled and errors can be checked.
        - (Default, debug 0) -
      • -
      • useSetExtensions (0,1)
        - Toggles the SetExtensions on or off.
        - (Default, useSetExtensions 0) -
      • -

      - Explanation: -
        -
      • abstract Internals:
      • -
          - xs1_function(1-4): defined function in the device
          - xs1_name: defined name in the device
          - xs1_typ: defined type in the device
          -

        -
      + Get
      +
        N/A

      + + Attributes +
        +
      • debug (0,1)
        + This brings the module into a very detailed debug output in the logfile. Thus, program parts can be controlled and errors can be checked.
        + (Default, debug 0) +
      • +
      • useSetExtensions (0,1)
        + Toggles the SetExtensions on or off.
        + (Default, useSetExtensions 0) +
      • +

      + Explanation: +
        +
      • abstract Internals:
      • +
          + xs1_function(1-4): defined function in the device
          + xs1_name: defined name in the device
          + xs1_typ: defined type in the device
          +

        +
    =end html =begin html_DE @@ -529,39 +513,39 @@ sub xs1Dev_Undef($$)

    xs1Dev

      - Dieses Modul arbeitet mit dem Modul xs1Bridge zusammen. (Das Attribut xs1_control im Modul xs1Bridge muss auf 1 gestellt sein!)
      - Es kommuniziert mit diesem und legt sämtliche Aktoren des xs1 als Device im FHEM an. So kann man vom FHEM aus, die Aktoren der xs1 steuern. -

      - Das Modul wurde entwickelt basierend auf dem Firmwarestand v4-Beta des xs1. Es kann aufgrund von unterschiedlichen Anpassungen innerhalb der Firmware des Herstellers zu Fehlern kommen.
      - -
        - Derzeit implementierte Typen des xs1 zur Verarbeitung:
        -
      • Aktor: dimmer, switch, shutter, timerswitch
      • -
      • Sensor: barometer, counter, counterdiff, light, motion, other, rain, rain_1h, rain_24h, rainintensity, remotecontrol, uv_index, waterdetector, winddirection, windgust, windspeed, windvariance
      • -


      + Dieses Modul arbeitet mit dem Modul xs1Bridge zusammen. (Das Attribut xs1_control im Modul xs1Bridge muss auf 1 gestellt sein!)
      + Es kommuniziert mit diesem und legt sämtliche Aktoren des xs1 als Device im FHEM an. So kann man vom FHEM aus, die Aktoren der xs1 steuern. +

      + Das Modul wurde entwickelt basierend auf dem Firmwarestand v4-Beta des xs1. Es kann aufgrund von unterschiedlichen Anpassungen innerhalb der Firmware des Herstellers zu Fehlern kommen.
      + +
        + Derzeit implementierte Typen des xs1 zur Verarbeitung:
        +
      • Aktor: dimmer, switch, shutter, timerswitch
      • +
      • Sensor: barometer, counter, counterdiff, light, motion, other, rain, rain_1h, rain_24h, rainintensity, remotecontrol, uv_index, waterdetector, winddirection, windgust, windspeed, windvariance
      • +


      - - Define
      -
        - define <name> xs1Dev <Typ> <ID> IODev=<NAME> -

        + + Define
        +
          + define <name> xs1Dev <Typ> <ID> IODev=<NAME> +

          - Ein anlegen des Modules ohne Angabe des Typ und der ID vom xs1 ist nicht möglich. -
            -
          • <ID> ist interne ID im xs1.
          • -
          -
            -
          • <Typ> ist der Kürzel A für Aktoren oder S für Sensoren.
          • -

          - Beispiel: -
            - define xs1Dev_Aktor_02 xs1Dev A 02 IODev=ezControl -
          -

        - Set -
          set <name> <value>

        - Wobei value der in der xs1 definierten Funktion entspricht. Bsp:
        -
          + Ein anlegen des Modules ohne Angabe des Typ und der ID vom xs1 ist nicht möglich. +
            +
          • <ID> ist interne ID im xs1.
          • +
          +
            +
          • <Typ> ist der Kürzel A für Aktoren oder S für Sensoren.
          • +

          + Beispiel: +
            + define xs1Dev_Aktor_02 xs1Dev A 02 IODev=ezControl +
          +

        + Set +
          set <name> <value>

        + Wobei value der in der xs1 definierten Funktion entspricht. Bsp:
        +
          an
          aus
          dimup
          @@ -570,37 +554,37 @@ sub xs1Dev_Undef($$) an, warten, aus
          absolut
          warten
          - langes AN
          - langes AUS
          - Stopp
          - an, warten, an
          - aus, warten, aus
          - Impuls
          + langes AN
          + langes AUS
          + Stopp
          + an, warten, an
          + aus, warten, aus
          + Impuls

        - Get
        -
          N/A

        - - Attribute -
          -
        • debug (0,1)
          - Dies bringt das Modul in eine sehr ausführliche Debug-Ausgabe im Logfile. Somit lassen sich Programmteile kontrollieren und Fehler überprüfen.
          - (Default, debug 0) -
        • -
        • useSetExtensions (0,1)
          - Schaltet die SetExtensions ein bzw. aus.
          - (Default, useSetExtensions 0) -
        • -

        - Erläuterung: -
          -
        • Auszug Internals:
        • -
            - xs1_function(1-4): definierte Funktion im Gerät
            - xs1_name: definierter Name im Gerät
            - xs1_typ: definierter Typ im Gerät
            -

          -
        - + Get
        +
          N/A

        + + Attribute +
          +
        • debug (0,1)
          + Dies bringt das Modul in eine sehr ausführliche Debug-Ausgabe im Logfile. Somit lassen sich Programmteile kontrollieren und Fehler überprüfen.
          + (Default, debug 0) +
        • +
        • useSetExtensions (0,1)
          + Schaltet die SetExtensions ein bzw. aus.
          + (Default, useSetExtensions 0) +
        • +

        + Erläuterung: +
          +
        • Auszug Internals:
        • +
            + xs1_function(1-4): definierte Funktion im Gerät
            + xs1_name: definierter Name im Gerät
            + xs1_typ: definierter Typ im Gerät
            +

          +
        +
      =end html_DE =cut