# $Id$ # # Siro module for FHEM # Thanks for templates/coding from SIGNALduino team and Jarnsen_darkmission_ralf9 # Thanks to Dr. Smag for decoding the protocol, which made this module possible # Needs SIGNALduino. # Published under GNU GPL License, v2 # History: # 30.05.19 Version 1.0 innitial comit ################################################################################################################ # Todo's: # - # - ############################################################################################################### package main; use strict; use warnings; my $version = "1.3"; sub Siro_Initialize($) { my ($hash) = @_; $hash->{SetFn} = "FHEM::Siro::Set"; $hash->{NotifyFn} = "FHEM::Siro::Notify"; $hash->{ShutdownFn} = "FHEM::Siro::Shutdown"; $hash->{FW_deviceOverview} = 1; $hash->{FW_detailFn} = "FHEM::Siro::fhemwebFn"; $hash->{DefFn} = "FHEM::Siro::Define"; $hash->{UndefFn} = "FHEM::Siro::Undef"; $hash->{DeleteFn} = "FHEM::Siro::Delete"; $hash->{ParseFn} = "FHEM::Siro::Parse"; $hash->{AttrFn} = "FHEM::Siro::Attr"; $hash->{Match} = "^P72#[A-Fa-f0-9]+"; $hash->{AsyncOutput} = "FHEM::Siro::AsyncOutput"; $hash->{AttrList} = " IODev" . " disable:0,1" . " SIRO_signalRepeats:1,2,3,4,5,6,7,8,9" . " SIRO_inversPosition:0,1" . " SIRO_Battery_low" . " SIRO_downLimit:slider,0,1,100" . " SIRO_signalLongStopRepeats:10,15,20,40,45,50" . " $readingFnAttributes" . " SIRO_send_channel:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15" . " SIRO_send_id" . " SIRO_time_to_open" . " SIRO_time_to_close" . " SIRO_debug:0,1" . " SIRO_no_IO_msg:0,1" . " SIRO_dbl_msg_block" . " SIRO_remote_correction:0,0.25,0.5,0.75,1,1.25,1.5,1.75,2,2.5,2.75,3" ; $hash->{AutoCreate} = { "Siro.*" => { #ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", autocreateThreshold => "2:10" } }; $hash->{helper}{progmode} = "off"; #exexcmd on } ############################# #### arbeiten mit packages package FHEM::Siro; use strict; use warnings; use GPUtils qw(GP_Import) ; # wird fuer den Import der FHEM Funktionen aus der fhem.pl ben?tigt ## Import der FHEM Funktionen BEGIN { GP_Import( qw(readingsSingleUpdate readingsBeginUpdate readingsEndUpdate readingsBulkUpdate defs modules Log3 AttrVal ReadingsVal IsDisabled gettimeofday InternalTimer RemoveInternalTimer AssignIoPort IOWrite ReadingsNum CommandAttr attr fhem init_done setDevAttrList readingFnAttributes devspec2array ) ); } my %codes = ( "55" => "stop", # Stop the current movement or move to custom position "11" => "off", # Move "up" "33" => "on", # Move "down" "CC" => "prog", # Programming-Mode (Remote-control-key: P2) ); my %sets = ( "open" => "noArg", "close" => "noArg", "up" => "noArg", "down" => "noArg", "off" => "noArg", "stop" => "noArg", "longstop" => "noArg", "on" => "noArg", "fav" => "noArg", "prog" => "noArg", "sequenz" => "noArg", "prog_mode_on" => "noArg", "prog_mode_off" => "noArg", "lock_remote" => "on,off", "lock_cmd" => "on,off", "reset_motor_term" => "noArg", "pct" => "slider,0,1,100", # Wird nur bei vorhandenen time_to attributen gesetzt "position" => "slider,0,1,100", # Wird nur bei vorhandenen time_to attributen gesetzt "set_favorite_position" => "slider,0,1,100", # manuelles setzen def fav position #"state" => "noArg", "set_favorite" => "noArg", "del_favorite" => "only_modul,only_shutter,shutter_and_modul", "down_for_timer" => "textField", "up_for_timer" => "textField" ); my %setsstandart = ( "open" => "noArg", "close" => "noArg", "up" => "noArg", "down" => "noArg", "off" => "noArg", "stop" => "noArg", "longstop" => "noArg", "on" => "noArg", "fav" => "noArg", "prog" => "noArg", "sequenz" => "noArg", "prog_mode_on" => "noArg", "prog_mode_off" => "noArg", "lock_remote" => "on,off", "lock_cmd" => "on,off", "reset_motor_term" => "noArg", "pct" => "slider,0,1,100", # Wird nur bei vorhandenen time_to attributen gesetzt "position" => "slider,0,1,100", # Wird nur bei vorhandenen time_to attributen gesetzt #"state" => "noArg", "Favorite-Position" => "slider,0,1,100", # manuelles setzen def fav position "set_favorite" => "noArg", "del_favorite" => "only_modul,only_shutter,shutter_and_modul", "down_for_timer" => "textField", "up_for_timer" => "textField" ); my %setszero = ( "open" => "noArg", "close" => "noArg", "up" => "noArg", "down" => "noArg", "off" => "noArg", "stop" => "noArg", "on" => "noArg", "fav" => "noArg", ); my %sendCommands = ( "pct" => "level", "level" => "level", "position" => "level", "stop" => "stop", "longstop" => "longstop", "off" => "off", "on" => "on", "open" => "off", "close" => "on", "up" => "off", "down" => "on", "fav" => "fav", "prog" => "prog", "reset_motor_term" => "reset_motor_term", "up_for_timer" => "upfortimer", "down_for_timer" => "downfortimer", "lock_remote" => "lock_remote", "Favorite-Position" => "favposition", "lock_cmd" => "lock_cmd" ); my %siro_c2b; # Map commands from web interface to codes used in Siro foreach my $k ( keys %codes ) { $siro_c2b{ $codes{$k} } = $k; } ############################# sub Attr(@) { my ( $cmd, $name, $aName, $aVal ) = @_; my $hash = $defs{$name}; return "\"Siro Attr: \" $name does not exist" if ( !defined($hash) ); if ( $cmd eq "set" and $init_done == 1) { if ( $aName eq "SIRO_inversPosition" ) { my $oldinvers = AttrVal($name,'SIRO_inversPosition','undef'); Log3( $name,5 , "Siro_attr_oldinvers: $oldinvers "); Log3( $name,5 , "Siro_attr_newinvers: $aVal "); if ( $aVal ne $oldinvers) { my $aktstate = ReadingsVal( $name, 'state', 'undef' ); $aktstate = 100 - $aktstate; readingsSingleUpdate( $hash, "state", $aktstate , 1 ); readingsSingleUpdate( $hash, "pct", $aktstate , 1 ); } } Log3( $name,5 , "Siro_attr: $cmd, $name, $aName, $aVal "); } Log3( $name,5 , "Siro_attr init done : $init_done"); return; } ############################# sub Define($$) { my ( $hash, $def ) = @_; my @a = split( "[ \t][ \t]*", $def ); my $u = "Wrong syntax: define Siro id "; my $askedchannel; # Angefragter kanal # Fail early and display syntax help if ( int(@a) < 3 ) { return $u; } if ( $a[2] =~ m/^[A-Fa-f0-9]{8}$/i ) { $hash->{ID} = uc( substr( $a[2], 0, 7 ) ); $hash->{CHANNEL_RECEIVE} = sprintf( "%d", hex( substr( $a[2], 7, 1 ) ) ); $askedchannel = sprintf( "%d", hex( substr( $a[2], 7, 1 ) ) ); } else { return "Define $a[0]: wrong address format: specify a 8 char hex value (id=7 chars, channel=1 char) . Example A23B7C51. The last hexchar identifies the channel. -> ID=A23B7C5, Channel=1. "; } $hash->{Version} = $version; my $name = $a[0]; my $code = uc( $a[2] ); my $ncode = 1; my $devpointer = $hash->{ID} . $hash->{CHANNEL_RECEIVE}; $hash->{CODE}{ $ncode++ } = $code; $hash->{MODEL} = "LE-serie"; $modules{Siro}{defptr}{$devpointer} = $hash; AssignIoPort($hash); # attributliste anlegen if ($hash->{CHANNEL_RECEIVE} eq '0') { my $attrzerolist = " IODev" . " disable:0,1" . " SIRO_signalRepeats:1,2,3,4,5,6,7,8,9" . " SIRO_signalLongStopRepeats:10,15,20,40,45,50" . " $readingFnAttributes" . " SIRO_debug:0,1"; $hash->{MODEL} = "LE-Group"; setDevAttrList($name, $attrzerolist); } else { setDevAttrList($name, $hash->{AttrList}); $hash->{MODEL} = "LE-Device"; } my $webcmd = "webCmd stop:open:close:fav:pct"; $webcmd = "webCmd stop:open:close:fav" if $hash->{CHANNEL_RECEIVE} eq '0'; CommandAttr( undef,$name . ' devStateIcon {return FHEM::Siro::Siro_icon($name)}' ) if ( AttrVal($name,'devStateIcon','none') eq 'none' ); CommandAttr(undef,$name . ' '.$webcmd) if ( AttrVal($name,'webCmd','none') eq 'none' ); Log3( $name, 5, "Siro_define: angelegtes Device - code -> $code name -> $name hash -> $hash " ); } ############################# sub Undef($$) { my ( $hash, $name ) = @_; delete( $modules{Siro}{defptr}{$hash} ); return undef; } ############################# sub Shutdown($) { my ($hash) = @_; my $name = $hash->{NAME}; return; } ############################# sub LoadHelper($) { my ($hash) = @_; my $name = $hash->{NAME}; return; } ############################# sub Notify($$) { return; } ############################# sub Delete($$) { my ( $hash, $name ) = @_; return undef; } ############################# sub SendCommand($@) { my ( $hash, @args ) = @_; my $ret = undef; my $cmd = $args[0]; # Command as text (on, off, stop, prog) my $message; # IO-Message (full) my $chan; # Channel my $binChannel; # Binary channel my $SignalRepeats; # my $name = $hash->{NAME}; my $binHash; my $bin; # Full binary IO-Message my $binCommand; my $numberOfArgs = int(@args); my $command = $siro_c2b{$cmd}; my $io = $hash->{IODev}; # IO-Device (SIGNALduino) Log3( $name, 5,"Siro_sendCommand: cmd - $cmd"); if ($cmd eq "longstop") { $args[1]="longstop"; $cmd="stop"; $command = $siro_c2b{$cmd}; } Log3( $name, 5,"Siro_sendCommand: args1 - $args[1]") if defined $args[1]; if ( defined($hash->{helper}{exexcmd}) and $hash->{helper}{exexcmd} eq "off") # send kommand blockiert / keine ausf?hrung { Log3( $name, 4,"Siro_sendCommand: ausfuehrung durch helper blockiert "); return; } if ( defined($hash->{helper}{ignorecmd}) and $hash->{helper}{ignorecmd} eq "on") # send kommand blockiert / keine ausf?hrung { Log3( $name, 4,"Siro_sendCommand: ausfuehrung einmalig blockiert "); delete( $hash->{helper}{ignorecmd} ); return; } #if ( (defined($args[1]) and $args[1] eq "longstop" )|| (defined $hash->{helper}{progmode} and $hash->{helper}{progmode} eq "on")) if ( (defined($args[1]) and $args[1] eq "longstop" )) { $SignalRepeats = AttrVal( $name, 'SIRO_signalLongStopRepeats', '15' ); } else { $SignalRepeats = AttrVal( $name, 'SIRO_signalRepeats', '10' ); } Log3( $name, 5,"Siro_sendCommand: repeats - $SignalRepeats"); $chan = AttrVal( $name, 'SIRO_send_channel', undef ); if ( !defined($chan) ) { $chan = $hash->{CHANNEL_RECEIVE}; } $binChannel = sprintf( "%04b", $chan ); my $value = $name . " " . join( " ", @args ); my $sendid = AttrVal( $name, 'SIRO_send_id', 'undef' ); if ( $sendid eq 'undef') { $binHash = sprintf( "%028b", hex( $hash->{ID} ) ); } else{ $binHash = sprintf( "%028b", hex( $sendid ) ); } Log3 $io, 5, "Siro_sendCommand: BinHash: = $binHash"; $binCommand = sprintf( "%08b", hex($command) ); Log3 $io, 5, "Siro_sendCommand: BinCommand: = $binCommand"; $bin = $binHash . $binChannel . $binCommand; # Binary code to send Log3 $io, 5, "Siro_sendCommand: Siro set value = $value"; $message = 'P72#' . $bin . '#R' . $SignalRepeats; IOWrite( $hash, 'sendMsg', $message ) if AttrVal( $name, 'SIRO_debug', "0" ) ne "1"; Log3( $name, 5,"Siro_sendCommand: name-$name command-$cmd channel-$chan bincmd-$binCommand bin-$bin id-$sendid message-$message"); Log3( $name, 3, "Siro_sendCommand: not sent upround debugmode 1") if AttrVal( $name, 'SIRO_debug', "0" ) eq "1"; return $ret; } ############################# sub Parse($$) { my @args; my ( $hash, $msg ) = @_; my $name = $hash->{NAME}; my $testid = substr( $msg, 4, 8 ); my $testcmd = substr( $msg, 12, 2 ); my $timediff; my $doubelmsgtime = AttrVal( $name, 'SIRO_dbl_msg_block',2 ); # zeit in sek in der doppelte nachrichten blockiert werden my $favcheck = $doubelmsgtime + 2;# zeit in der ein zweiter stop kommen muss/darf für fav # Log3( $name, 0,"name $name"); return "" if ( IsDisabled($name) ); #my $devpointer = $hash->{ID} . $hash->{CHANNEL_RECEIVE}; if ( my $lh = $modules{Siro}{defptr}{$testid} ) { my $name = $lh->{NAME}; #Log3( $name, 0,"name1 $name"); if ( defined($name)&& $testcmd ne "54")# pr?fe auf doppele msg falls ger?t vorhanden und cmd nicht stop { #Log3 $lh, 5,"Siro_Parse: Incomming msg $msg from IODevice name/DEF $testid - Hash -> $lh"; Log3( $name, 5,"Siro_Parse: Incomming msg $msg from IODevice name/DEF $testid - Hash -> $lh"); my $testparsetime = gettimeofday(); my $lastparse = $lh->{helper}{lastparse}; my @lastparsearray = split( / /, $lastparse ); if ( !defined( $lastparsearray[1] ) ) { $lastparsearray[1] = 0 } if ( !defined( $lastparsearray[0] ) ) { $lastparsearray[0] = "" } $timediff = $testparsetime - $lastparsearray[1]; my $abort = "false"; Log3( $name, 5,"Siro_Parse: test doublemsg "); Log3( $name, 5,"Siro_Parse: lastparsearray[0] -> $lastparsearray[0] "); Log3( $name, 5,"Siro_Parse: lastparsearray[1] -> $lastparsearray[1] "); Log3( $name, 5,"Siro_Parse: testparsetime -> $testparsetime "); Log3( $name, 5,"Siro_Parse: timediff -> $timediff "); if ( $msg eq $lastparsearray[0] ) { if ( $timediff < $doubelmsgtime ) { $abort = "true"; } } $lh->{helper}{lastparse} = "$msg $testparsetime"; if ( $abort eq "true" ) { Log3( $name, 5,"Siro_Parse: aborted , doublemsg "); return $name; } Log3( $name, 5,"Siro_Parse: not aborted , no doublemsg "); } my ( undef, $rawData ) = split( "#", $msg ); my $hlen = length($rawData); my $blen = $hlen * 4; my $bitData = unpack( "B$blen", pack( "H$hlen", $rawData ) ); Log3( $name, 5,"Siro_Parse: msg = $rawData length: $msg"); Log3( $name, 5,"Siro_Parse: rawData = $rawData length: $hlen"); Log3( $name, 5,"Siro_Parse: converted to bits: $bitData"); my $id = substr( $rawData, 0, 7 ); # The first 7 hexcodes are the ID my $BitChannel = substr( $bitData, 28, 4 ); # Not needed atm my $channel = sprintf( "%d", hex( substr( $rawData, 7, 1 ) ) );# The last hexcode-char defines the channel my $channelhex = substr( $rawData, 7, 1 ); # tmp my $cmd = sprintf( "%d", hex( substr( $rawData, 8, 1 ) ) ); my $newstate = $codes{ $cmd . $cmd }; # Set new state my $deviceCode = $id. $channelhex;#Tmp change channel -> channelhex. The device-code is a combination of id and channel Log3( $name, 5,"Siro_Parse: device ID: $id"); Log3( $name, 5,"Siro_Parse: Channel: $channel"); Log3( $name, 5,"Siro_Parse: Cmd: $cmd Newstate: $newstate"); Log3( $name, 5,"Siro_Parse: deviceCode: $deviceCode"); #if ( defined($name)&& $testcmd eq "54" )#prüfe auf doppele msg falls gerät vorhanden und cmd stop if ( defined($name)&& $testcmd eq "54" )#pr?fe auf doppele msg falls ger?t vorhanden und cmd stop { Log3( $name, 5,"Siro_Parse: prüfung auf douplestop "); Log3( $name, 5,"################################ "); my $testparsetime = gettimeofday(); my $lastparsestop = 0; $lastparsestop = $lh->{helper}{lastparse_stop} if defined $lh->{helper}{lastparse_stop}; my $parseaborted = $lh->{helper}{parse_aborted}; my @lastparsestoparray = split( / /, $lastparsestop ); my $timediff = $testparsetime - $lastparsestoparray[1]; my $abort = "false"; $parseaborted = 0 if ( !defined($parseaborted) ); Log3( $name, 5,"Siro_Parse: test doublestop "); Log3( $name, 5,"Siro_Parse: lastparsearray[0] -> $lastparsestoparray[0] "); Log3( $name, 5,"Siro_Parse: lastparsearray[1] -> $lastparsestoparray[1] "); Log3( $name, 5,"Siro_Parse: testparsetime -> $testparsetime "); Log3( $name, 5,"Siro_Parse: timediff -> $timediff "); Log3( $name, 5,"Siro_Parse: parseaborted -> $parseaborted "); if ( $newstate eq $lastparsestoparray[0] ) { if ( $timediff < 3 ) { $abort = "true"; $parseaborted++; } } if ( $abort eq "true" && $parseaborted < 2 ) { $lh->{helper}{parse_aborted} = $parseaborted; Log3( $name, 5,"Siro_Parse: aborted , doublestop "); return $name; } $lh->{helper}{lastparse_stop} = "$newstate $testparsetime"; if ( $parseaborted >= 2 ) { $parseaborted = 0; $lh->{helper}{parse_aborted} = $parseaborted; $testparsetime = gettimeofday(); $lh->{helper}{lastparse_stop} = "$newstate $testparsetime"; if ( $newstate eq "stop" ) { Log3( $name, 5,"Siro_Parse: double_msg signal_favoritenanfahrt erkannt "); $newstate = "fav"; $args[0] = "fav"; } } } Log3( $name, 5, "Siro Parse Befehl: $newstate"); if ($lh->{helper}{progmode} eq "on") { Log3( $name, 4, "Siro Parse deactivated cause off programmingmode"); return; } if ( defined($name) ) {#device vorhanden my $parseaborted = 0; $lh->{helper}{parse_aborted} = $parseaborted; Log3( $name, 5,"Siro_ Parse: $name $newstate"); my $defchannnel = $lh->{CHANNEL_RECEIVE}; my $atrrchannel = AttrVal( $name, 'SIRO_send_channel', $defchannnel ); Log3( $name, 5,"Siro_Parse: defchannnel - $defchannnel "); Log3( $name, 5,"Siro_Parse: atrrchannel - $atrrchannel "); if ($defchannnel eq $atrrchannel) { Log3( $name, 5,"Siro_Parse: setze remotecmd on "); $lh->{helper}{remotecmd} = "on"; #verhindert das senden von signalen nur wenn nicht auf anderem kanal gesendet wird } my $aktstate = ReadingsVal( $name, 'state', '0' ); my $lock =ReadingsVal( $name, 'lock_remote', 'off' ); if ($defchannnel ne '0') { Log3( $name, 5,"Siro_Parse: lock = $lock "); Log3( $name, 5,"Siro_Parse: newstate = $newstate "); # if ($lock ne 'on') { Log3( $name, 5,"Siro_Parse: weiterleitung -> set "); Set( $lh, $name, $newstate ); } # if ($lock eq 'on' and ($newstate eq 'fav' or $newstate eq 'stop')) { Log3( $name, 5,"Siro_Parse: weiterleitung -> set "); Set( $lh, $name, $newstate ); } elsif ($lock eq 'on' and $aktstate ne "0" and $aktstate ne "100") { Log3( $name, 5,"Siro_Parse: weiterleitung -> set "); Set( $lh, $name, $newstate ); } } if ($defchannnel eq '0') { Log3( $name, 5,"Siro_Parse: eingehender Gruppenbefehl , weiterleitung an deviceverteiler "); Log3( $name, 5,"Siro_Parse: eingehender Gruppenbefehl $newstate"); readingsSingleUpdate( $lh, "ActionTrigger", "remote", 1 ); Distributor($name.' '.$newstate.' '.$testcmd); return $name; } Log3( $name, 4, "Siro_Parse: hash->{helper}{remotecmd} - ".$lh->{helper}{remotecmd}); Log3( $name, 3, "Siro-Parse ($name) : Signal FB emfangen - $newstate"); Log3( $name, 4, "Siro-Parse ($name) : test lock_remote - $lock"); if ($lock eq 'on') # remotelock { Log3( $name, 4, "Siro-Parse ($name) : founde lock_remote - target: $aktstate"); if ($aktstate eq "0") { $lh->{helper}{remotecmd} = "off"; $lh->{helper}{exexcmd}="on"; Log3( $name, 4, "Siro-Parse ($name) :send comand aufruf off"); SendCommand( $lh, 'off' ); $lh->{helper}{savedcmds}{cmd1} = 'open' ; InternalTimer( (time + 3), "FHEM::Siro::Restartset", "$name" ); return; } elsif ($aktstate eq "100") { $lh->{helper}{remotecmd} = "off"; $lh->{helper}{exexcmd}="on"; Log3( $name, 4, "Siro-Parse ($name) :send comand aufruf on"); SendCommand( $lh, 'on' ); $lh->{helper}{savedcmds}{cmd1} = 'close' ; InternalTimer( (time + 3), "FHEM::Siro::Restartset", "$name" ); return; } else { Set( $lh, $name, 'stop'); $lh->{helper}{savedcmds}{cmd1} = 'pct' ; $lh->{helper}{savedcmds}{cmd2} = $aktstate; RemoveInternalTimer("FHEM::Siro::Restartset", "$name"); InternalTimer( (time + 1), "FHEM::Siro::Restartset", "$name" ); return; } } return $name; } } else { # device nicht vorhanden my ( undef, $rawData ) = split( "#", $msg ); my $hlen = length($rawData); my $blen = $hlen * 4; my $bitData = unpack( "B$blen", pack( "H$hlen", $rawData ) ); Log3 $hash, 5, "Siro_Parse: msg = $rawData length: $msg"; Log3 $hash, 5, "Siro_Parse: rawData = $rawData length: $hlen"; Log3 $hash, 5, "Siro_Parse: converted to bits: $bitData"; my $id = substr( $rawData, 0, 7 ); # The first 7 hexcodes are the ID my $BitChannel = substr( $bitData, 28, 4 ); # Not needed atm my $channel = sprintf( "%d", hex( substr( $rawData, 7, 1 ) ) ); # The last hexcode-char defines the channel my $channelhex = substr( $rawData, 7, 1 ); # tmp my $cmd = sprintf( "%d", hex( substr( $rawData, 8, 1 ) ) ); my $newstate = $codes{ $cmd . $cmd }; # Set new state my $deviceCode = $id. $channelhex; # Tmp change channel -> channelhex. The device-code is a combination of id and channel Log3 $hash, 5, "Siro_Parse: device ID: $id"; Log3 $hash, 5, "Siro_Parse: Channel: $channel"; Log3 $hash, 5, "Siro_Parse: Cmd: $cmd Newstate: $newstate"; Log3 $hash, 5, "Siro_Parse: deviceCode: $deviceCode"; Log3 $hash, 2, "Siro unknown device $deviceCode, please define it"; return "UNDEFINED Siro_$deviceCode Siro $deviceCode"; } } ############################# # Call with hash, name, virtual/send, set-args sub Set($@) { my $testtimestart = gettimeofday(); my $debug; my ( $hash, $name, @args ) = @_; my $cmd = $args[0]; # eingehendes set my $zielposition = $args[1]; # eingehendes set position my $param = $args[1]; # eingehendes set position $param = "" if !defined $param; Log3( $name, 5, "Siro-Set: eingehendes Kommando $cmd") if $cmd ne "?"; ### check for old version if (ReadingsVal( $name, 'last_reset_os', 'undef' ) ne 'undef' && $cmd ne "?") { Log3( $name,0 , "Das Siromodul wurde geaendert und die einstellungen sind nicht mehr Kompatibel. Bitte das Sirodevice \"$name\" kontrollieren ."); } ############################# my $actiontime = time; # zeit dieses Aufrufes my $lastactiontime = ReadingsVal( $name, 'ActionTime', $actiontime ); # Zeit des letzten Aufrufes my $betweentime = $actiontime-$lastactiontime; # Zeit zwischen aktuellem und letztem Aufruf my $downtime = AttrVal( $name, 'SIRO_time_to_close','undef' ); # fahrdauer runter my $uptime = AttrVal( $name, 'SIRO_time_to_open','undef' ); # fahrdauer hoch my $correction = AttrVal( $name, 'SIRO_remote_correction',0 ); # zeitkorrektur fernbedienung my $down1time ="undef"; # fahrzeit 1 prozent my $up1time ="undef"; # fahrzeit 1 prozent my $drivingtime; # fahrzeit bei positionsanfahrt my $aktendaction = ReadingsVal( $name, 'aktEndAction', '0' ); #endzeit laufende avtion my $akttimeaction = ReadingsVal( $name, 'aktTimeAction', '0' ); #dauer einer laufenden aktion my $aktrunningaction = ReadingsVal( $name, 'aktRunningAction', '' ); #typ einer laufenden aktion my $position = ReadingsVal( $name, 'pct', '' ); #position pct bis zum ende einer aktion my $state = ReadingsVal( $name, 'state', 'undef' ); #aktuelle aktion ( runningDown/runningUp ) my $drivedpercents; # beinhaltet gefahrene prozent bei aktionswechsel my $newposition ; # beinhaltet neue positin bei aktionswechsel my $favposition = ReadingsVal( $name, 'Favorite-Position', 'nA' ); #gespeicherte Favoritenposition my $invers = 1; #invertiert position my $oldcmdfrom = ReadingsVal( $name, 'ActionTrigger', 'fhem' );# ActionTrigger der letzten aktion my $defchannnel = $hash->{CHANNEL_RECEIVE}; if ($downtime ne "undef" && $uptime ne "undef") { $down1time = $downtime/100; $up1time = $uptime/100; } return "" if ( IsDisabled($name) ); $hash->{helper}{progmode} = "off" if !defined $hash->{helper}{progmode}; # versionschange #changeconfig # pruefe auf unbekannte sets if ( $cmd =~ m/^exec.*/ )# empfangene sequenz aus programmiermode { $args[1] = $cmd; $cmd = "sequenz"; } if ($defchannnel eq '0') { %sets = %setszero; } else { %sets = %setsstandart; } if ( !exists( $sets{$cmd} ) ) { my @cList; my $atts = AttrVal( $name, 'setList', "" ); my %setlist = split( "[: ][ ]*", $atts ); foreach my $k ( sort keys %sets ) { my $opts = undef; $opts = $sets{$k}; $opts = $setlist{$k} if ( exists( $setlist{$k} ) ); if ( defined($opts) ) { push( @cList, $k . ':' . $opts ); } else { push( @cList, $k ); } } # end foreach return "Unknown argument $cmd, choose one of " . join( " ", @cList ); } ################## if ($defchannnel eq '0') { Log3( $name, 5, "Siro-def0: newstate $cmd"); Log3( $name, 5, "Siro-def0: testcmd ".$sets{$cmd}); Distributor($name.' '.$cmd.' '.$sets{$cmd}); readingsSingleUpdate( $hash, "ActionTrigger", "fhem", 1 ); # name befehl befehlscode $hash->{helper}{exexcmd}="on"; Log3( $name, 5, "Siro-def0: sende kommando ".$sendCommands{$cmd}); if ($sendCommands{$cmd} eq 'fav'){ SendCommand( $hash, 'stop' , 'longstop' );} else{ SendCommand( $hash, $sendCommands{$cmd} );} return; } ############################# # programmiermodus ############################# #if ( $hash->{helper}{progmode} eq "on" && $cmd eq "sequenz") # sequenz ausführen if ( defined $hash->{helper}{progmode} and $hash->{helper}{progmode} eq "on" && $cmd eq "sequenz") # sequenz ausf?hren { Log3( $name, 5, "Siro-Programmiermodus: Sequenz gefunden :$args[1]"); my @seq = split(/,/, $args[1]); shift @seq; my $exectime = time; foreach my $seqpart (@seq) { #$actiontime $exectime = $exectime+2; my $execcmd = $seqpart; Log3( $name, 5, "Siro-Programmiermodus: Sequenz - $exectime - $execcmd"); InternalTimer( $exectime, "FHEM::Siro::Prog", $name." ".$execcmd ); } return; } if ($cmd eq "prog_mode_on" && $hash->{helper}{progmode} ne "on") { readingsSingleUpdate( $hash, "state", 'programming', 1 ); $hash->{helper}{progmode} = "on"; } if ($cmd eq "prog_mode_off" && $hash->{helper}{progmode} eq "on") { readingsSingleUpdate( $hash, "state", $position, 1 ); #delete( $hash->{helper}{progmode} ); $hash->{helper}{progmode} = "off"; } if ($hash->{helper}{progmode} eq "on") { Log3( $name, 5, "Siro-Programmiermodus: ".$sendCommands{$cmd}); SendCommand( $hash, $sendCommands{$cmd} ); delete( $hash->{Signalduino_RAWMSG} ); delete( $hash->{Signalduino_MSGCNT} ); delete( $hash->{Signalduino_RSSI} ); delete( $hash->{Signalduino_TIME} ); Log3( $name, 5, "Siro-Programmiermodus: Parse deaktiviert"); return; } ############################# if ($state eq "programming") # keine Befehlsausf?hrung w?hrend einer programmierung { Log3( $name, 1, "Siro-Programmiermodus: Befehl nicht moeglich , Device ist im Programmiermodus"); return; } # setze actiontime und lastactiontime # umbauen zu bulk update RemoveInternalTimer($name); #alle vorhandenen timer l?schen #delete( $hash->{helper}{exexcmd} ); # on/off off blockiert befehlsausf?hrung / l?schen vor jedem durchgang $hash->{helper}{exexcmd}="on"; #reset ignore send comand states $hash->{helper}{ignorecmd} = "off" ; #reset ignore send comand states #setze helper neu wenn signal von fb kommt #if ($hash->{helper}{remotecmd} eq "on") ############################# my $aktcmdfrom ="fhem"; if ( defined($hash->{helper}{remotecmd}) and $hash->{helper}{remotecmd} eq "on") { $hash->{helper}{exexcmd} = "off" ; $aktcmdfrom = "remote"; } delete( $hash->{helper}{remotecmd} ); ############################# # befehl ist von distributor abgesetzt - kam von kanal 0 Log3( $name, 5, "Siro-Set: param - $param"); if (defined $param and $param eq "fakeremote") { $hash->{helper}{exexcmd} = "off" ; $aktcmdfrom = "remote"; } ############################# readingsBeginUpdate($hash); readingsBulkUpdate( $hash, "ActionTrigger", $aktcmdfrom, 1 ); readingsBulkUpdate( $hash, "LastActionTime", $lastactiontime, 0 ); readingsBulkUpdate( $hash, "BetweentActionTime", $betweentime, 0 ); readingsBulkUpdate( $hash, "ActionTime", $actiontime, 0 ); readingsEndUpdate($hash, 1); # befehl aus %sendCommands ermitteln my $comand = $sendCommands{$cmd}; # auzuf?hrender befehl Log3( $name, 5, "Siro-Set: ermittelter Befehl: $comand " ) if defined $comand; ############################# # limit testen , falls limit wird on zu level limit my $downlimit = AttrVal( $name, 'SIRO_downLimit','undef' ) ; if ($downlimit ne "undef" && ($comand eq 'on' || $comand eq 'level') && $hash->{helper}{exexcmd} ne "off") # nur wenn befehl nicht von fb kommt { if (!defined $zielposition){$zielposition = 100} if ( $position < $downlimit ) { $comand = 'level'; $zielposition = $downlimit; } my $sendchan = AttrVal( $name, 'SIRO_send_channel', 'undef' ); if ( $sendchan ne $hash->{CHANNEL_RECEIVE} && $position >= $downlimit ) { return; } } ############################# if ($downlimit ne "undef" && ($comand eq 'on' || $comand eq 'level') && $hash->{helper}{exexcmd} eq "off") # nur wenn befehl von fb kommt { if ( $position < $downlimit ) { #delete( $hash->{helper}{exexcmd} ); $hash->{helper}{exexcmd}="on"; $hash->{helper}{ignorecmd} ="on"; $comand = 'level'; $zielposition = $downlimit; } } ############################# lock_remote if ($comand eq "lock_remote") { readingsSingleUpdate( $hash, "lock_remote", $args[1], 1 ) ; if (ReadingsVal( $name, 'lock_cmd', 'off' ) eq "on" and $args[1] eq "on") { readingsSingleUpdate( $hash, "lock_remote", "off", 1 ) ; Log3( $name, 1, "Siro-Set: Lock Remote ist nicht moeglich, wenn Lock CMD aktiviert ist"); return "Lock Remote ist nicht moeglich, wenn Lock CMD aktiviert ist"; } return; } ############################# lock_cmd if ($comand eq "lock_cmd") { readingsSingleUpdate( $hash, "lock_cmd", $args[1], 1 ) ; if (ReadingsVal( $name, 'lock_remote', 'off' ) eq "on" and $args[1] eq "on") { readingsSingleUpdate( $hash, "lock_cmd", "off", 1 ) ; Log3( $name, 1, "Siro-Set: Lock Cmd ist nicht moeglich, wenn Lock Remote aktiviert ist"); return "Lock Cmd ist nicht moeglich, wenn Lock Remote aktiviert ist"; } return; } if (ReadingsVal( $name, 'lock_cmd', 'off' ) eq 'on' and $param ne "fakeremote" and $hash->{helper}{exexcmd} eq "on" ) { Log3( $name, 3, "Siro-Set: angefragte Aktion $comand abgebrochen (lock_cmd -> on)"); readingsSingleUpdate( $hash, "pct", $position , 1 ); readingsSingleUpdate( $hash, "position", $position , 1 ); return; } ############################# # set reset_motor_term reset_motor_term if ($comand eq "reset_motor_term") { readingsSingleUpdate( $hash, "motor-term", "0", 1 ) ; readingsSingleUpdate( $hash, "batteryState", "unknown", 1 ) ; readingsSingleUpdate( $hash, "motor-term-reset", time, 1 ); return; } # pruefe auf laufende aktion nur bei definierten laufzeiten # wenn vorhanden neuberechnung aller readings if ($aktendaction > time && ($downtime ne "undef" || $uptime ne "undef")) { Log3( $name, 5, "Siro-Set: laufende aktion gefunden - abbruch"); Log3( $name, 5, "Siro-Set: laufende aktion -"); #aktTimeAction - dauer der laufenden aktion - in variable $akttimeaction #aktEndAction geplantes aktionsende - in variabel $aktendaction #$actiontime aktuelle zeit #$aktrunningaction - typ der laufenden aktion #$position -position bei actionsbeginn my $pastaction = $akttimeaction - ($aktendaction - $actiontime); Log3( $name, 5, "Siro-Set: unterbrochene Aktion $state lief $pastaction "); ##################korrektur zeitdifferenz fb/fhem if ($oldcmdfrom eq "remote" and $aktcmdfrom eq "fhem") { $pastaction = $pastaction + $correction ; Log3( $name, 5, "Siro-Set: unterbrochene Aktion wurde von $oldcmdfrom gestartet und von $aktcmdfrom unterbrochen, starte Korrektur "); Log3( $name, 5, "Siro-Set: unterbrochene Aktion $state lief $pastaction mit Korrektur"); Log3( $name, 5, "Siro-Set: Korrektur um $correction sekunden"); } if ($oldcmdfrom eq "fhem" and $aktcmdfrom eq "remote") { $pastaction = $pastaction - $correction ; Log3( $name, 5, "Siro-Set: unterbrochene Aktion wurde von $oldcmdfrom gestartet und von $aktcmdfrom unterbrochen, starte Korrektur "); Log3( $name, 5, "Siro-Set: unterbrochene Aktion $state lief $pastaction mit Korrektur"); Log3( $name, 5, "Siro-Set: Korrektur um $correction sekunden"); } ############################# Log3( $name, 5, "Siro-Set: Aktionsbeginn bei $position "); if ($state eq "runningDown" || $state eq "runningDownfortimer") { $drivedpercents = $pastaction/$down1time; $drivedpercents = ( int( $drivedpercents * 10 ) / 10 ); Log3( $name, 5, "Siro-Set: Positionsveraenderung um $drivedpercents Prozent nach unten "); if (AttrVal( $name, 'SIRO_inversPosition',0 ) eq "1") { $newposition = int ($position-$drivedpercents); } else{ $newposition = int ($position+$drivedpercents); } } if ($state eq "runningUp" || $state eq "runningUpfortimer") { $drivedpercents = $pastaction/$up1time; $drivedpercents = ( int( $drivedpercents * 10 ) / 10 ); Log3( $name, 5, "Siro-Set: Positionsveraenderung um $drivedpercents Prozent nach oben "); if (AttrVal( $name, 'SIRO_inversPosition',0 ) eq "1") { $newposition = int ($position+$drivedpercents); } else{ $newposition = int ($position-$drivedpercents); } } Log3( $name, 5, "Siro-Set: neue Position - $newposition "); my $operationtime = ReadingsNum( $name, 'motor-term', 0 ); my $newoperationtime = $operationtime + $pastaction; readingsBeginUpdate($hash); readingsBulkUpdate( $hash, "state", $newposition ) ; readingsBulkUpdate( $hash, "pct", $newposition ) ; readingsBulkUpdate( $hash, "position", $newposition) ; readingsBulkUpdate( $hash, "aktRunningAction", "noAction" ) ; readingsBulkUpdate( $hash, "aktEndAction", 0 ) ; readingsBulkUpdate( $hash, "aktTimeAction", 0 ) ; readingsBulkUpdate( $hash, "aktActionFinish", 0 ) ; readingsBulkUpdate( $hash, "motor-term", $newoperationtime, 1 ) ; readingsEndUpdate($hash, 1); if (defined $comand and $comand ne "stop") #wenn anders kommando als stop befehl zwischenspeichern und per internal timer neu aufrufen , vorher fahrt stoppen per befehl. Stopbefehl l?uft durch wegen notbetrieb ohne timer attribute, gespeicherter befehl wir abgelegt in reading ($cmd) helper/cmd1 und ($zielposition) helper/cmd2. bei aufruf set wird auf vorhandensein gepr?ft. { SendCommand( $hash, 'stop' ); Log3( $name, 5, "Siro-Set: Twischenspeichern von Cmd ($cmd) und Position ($zielposition)"); $hash->{helper}{savedcmds}{cmd1} = $cmd; $hash->{helper}{savedcmds}{cmd2} = $zielposition if defined $zielposition; InternalTimer( time, "FHEM::Siro::Restartset", "$name" ); return; } } Log3( $name, 5, "Siro-Set: cmd nach change : $comand") if defined $comand;; ############################# #pct 100 und pct 0 auf on oder off mappen if (defined $comand and $comand eq "level" and $zielposition eq "100") { $comand = "on" if AttrVal( $name, 'SIRO_inversPosition',0 ) eq "0"; $comand = "off" if AttrVal( $name, 'SIRO_inversPosition',0 ) eq "1"; Log3( $name, 4, "Siro-Set: mapping level 100 - on"); } if (defined $comand and $comand eq "level" and $zielposition eq "0") { $comand = "off" if AttrVal( $name, 'SIRO_inversPosition',0 ) eq "0"; $comand = "on" if AttrVal( $name, 'SIRO_inversPosition',0 ) eq "1"; Log3( $name, 4, "Siro-Set: mapping level 0 - off"); } ############################# # mappe invers position # verschoben in routine on/off ############################# on off for timer # up/down for timer mappen auf on/off und timer für stop setzen if (defined $comand and $comand eq 'upfortimer' ) { Log3( $name, 5, "Siro-Set: up_for_timer $args[1]" ); $hash->{helper}{savedcmds}{cmd1} = 'stop'; InternalTimer( time + $args[1], "FHEM::Siro::Restartset", "$name" ); } ############################# on off for timer # up/down for timer mappen auf on/off und timer für stop setzen if (defined $comand and $comand eq 'downfortimer' ) { Log3( $name, 5, "Siro_Set: down_for_timer $args[1]" ); $hash->{helper}{savedcmds}{cmd1} = 'stop'; InternalTimer( time + $args[1], "FHEM::Siro::Restartset", "$name" ); } ############################# if (defined $comand and $comand eq "fav") # favoritenanfahrt { if ($favposition eq "nA") { Log3( $name, 1, "Siro-Set: Favoritenanfahrt nicht m?glich , Reading nicht gesetzt"); return; } Log3( $name, 3, "Siro-Set ($name) : set Favorit"); SendCommand( $hash, 'stop' , 'longstop' ); # befehl ?ndern auf position favorite # weiterer programmdurchlauf # per defintition keine weiteren send kommandos helper exexcmd on/off (off) $hash->{helper}{exexcmd} = 'off'; # schaltet das senden folgender befehls ab / nur anpassung der readings $comand = "level"; $zielposition = $favposition; } ############################# # favoritenposition speichern if ( $cmd eq "set_favorite" ) { # lockdevive einrichten ! readingsSingleUpdate( $hash, "state", 'programming', 1 ); my $sequence ; my $blocking; if ($favposition eq "nA") { $sequence = '1:prog,3:stop,3:stop' ; $blocking = 8; } else { $sequence = '1:prog,3:stop,3:stop,3:prog,3:stop,3:stop' ; $blocking =17; } my @sequenzraw =split (/,/,$sequence); my $exectime = $actiontime; foreach my $seqpart (@sequenzraw) { Log3( $name, 5, "Siro-Set: Favorit seqpart - $seqpart"); my @seqpartraw =split (/\:/,$seqpart); #$actiontime $exectime = $exectime+$seqpartraw[0]; my $execcmd = $seqpartraw[1]; Log3( $name, 5, "Siro-Set: Favorit $exectime - $execcmd"); InternalTimer( $exectime, "FHEM::Siro::Prog", $name." ".$execcmd ); InternalTimer( ($actiontime+$blocking), "FHEM::Siro::Delock", $name ); } readingsSingleUpdate( $hash, "Favorite-Position", $position, 1 ); return; } ############################# # favoritenposition speichern if ( $cmd eq "del_favorite" ) { if ($args[1] eq "only_shutter" || $args[1] eq "shutter_and_modul") { readingsSingleUpdate( $hash, "state", 'programming', 1 ); my $sequence ; $sequence = '0:prog,2:stop,2:stop' ; my @sequenzraw =split (/,/,$sequence); my $exectime = $actiontime; foreach my $seqpart (@sequenzraw) { Log3( $name, 5, "Siro-Set: Delfavorit seqpart - $seqpart"); my @seqpartraw =split (/\:/,$seqpart); #$actiontime $exectime = $exectime+$seqpartraw[0]; my $execcmd = $seqpartraw[1]; Log3( $name, 5, "Siro-Set: Delfavorit $exectime - $execcmd"); InternalTimer( $exectime, "FHEM::Siro::Prog", $name." ".$execcmd ); InternalTimer( $exectime+10, "FHEM::Siro::Delock", $name ); } } if ($args[1] eq "only_modul" || $args[1] eq "shutter_and_modul") { readingsSingleUpdate( $hash, "Favorite-Position", 'nA', 1 ); } return; } ################# # favoritenposition anpassen if ( $cmd eq "Favorite-Position" ) { Log3( $name, 5, "Siro-Set: save favposition -> $args[1]"); readingsSingleUpdate( $hash, "Favorite-Position", $args[1], 1 ); return; } ############################# # set on ( device faehrt runter ) if ($comand eq "on" || $comand eq "downfortimer" ) { Log3( $name, 3, "Siro-Set ($name) : set Down"); if ($downtime eq "undef" || $uptime eq "undef") # bei ungesetzten fahrzeiten { readingsBeginUpdate($hash); readingsBulkUpdate( $hash, "state", "100" ) ; readingsBulkUpdate( $hash, "pct", "100" ) ; readingsBulkUpdate( $hash, "motor-term", "Function is not available without set runtime attribute, please define") ; readingsBulkUpdate( $hash, "LastAction", $comand ); readingsEndUpdate( $hash, 1); SendCommand( $hash, 'on' ); } if ($state eq "undef" || $state eq "notAvaible") { $state = 0; } my $waytodrive = 100 - $state; if (AttrVal( $name, 'SIRO_inversPosition',0 ) eq "1"){$waytodrive = $state;} my $timetodrive = $waytodrive * $down1time; my $endaction = time + $timetodrive; Log3( $name, 5, "Siro-Set: on downtime - waytodrive $waytodrive"); Log3( $name, 5, "Siro-Set: on downtime - state $state"); Log3( $name, 5, "Siro-Set: on downtime - down1time $down1time"); SendCommand( $hash, 'on' ); #SendCommand( $hash, 'stop' ); readingsBeginUpdate($hash); readingsBulkUpdate( $hash, "aktRunningAction", $comand ) ; readingsBulkUpdate( $hash, "aktEndAction", $endaction ) ; readingsBulkUpdate( $hash, "aktTimeAction", $timetodrive ) ; readingsBulkUpdate( $hash, "aktActionFinish", "100" ) ; readingsBulkUpdate( $hash, "LastAction", $comand ); readingsEndUpdate( $hash, 1); if ($comand eq "on") { readingsSingleUpdate( $hash, "state", "runningDown" , 1 ) ; # internen timer setzen runningtime - dann states setzen Log3( $name, 5, "Siro-Set: setze state down , setze Timer - $comand"); InternalTimer( $endaction, "FHEM::Siro::Finish", "$name" ); } else{ readingsSingleUpdate( $hash, "state", "runningDownfortimer" , 1 ) ; } #befehl ausfuhren } ############################# # set off ( device faeht hoch ) if ($comand eq "off" || $comand eq "upfortimer" ) { Log3( $name, 3, "Siro-Set ($name) : set Up"); if ($downtime eq "undef" || $uptime eq "undef") # bei ungesetzten fahrzeiten { readingsBeginUpdate($hash); readingsBulkUpdate( $hash, "state", "0" ) ; readingsBulkUpdate( $hash, "pct", "0" ) ; readingsBulkUpdate( $hash, "motor-term", "Function is not available without set runtime attribute, please define") ; readingsBulkUpdate( $hash, "LastAction", $comand ); readingsEndUpdate( $hash, 1); SendCommand( $hash, 'off' ); #return; } # if ($state eq "undef" || $state eq "notAvaible") { $state = 100; } my $waytodrive = 0 + $state; if (AttrVal( $name, 'SIRO_inversPosition',0 ) eq "1"){$waytodrive = 0 + (100- $state);} my $timetodrive = $waytodrive * $up1time; my $endaction = time + $timetodrive; Log3( $name, 5, "Siro-Set: off downtime - waytodrive $waytodrive"); Log3( $name, 5, "Siro-Set: off downtime - state $state"); Log3( $name, 5, "Siro-Set: off downtime - up1time $up1time"); SendCommand( $hash, 'off' ); readingsBeginUpdate($hash); readingsBulkUpdate( $hash, "aktRunningAction", $comand ) ; readingsBulkUpdate( $hash, "aktEndAction", $endaction ) ; readingsBulkUpdate( $hash, "aktTimeAction", $timetodrive ) ; readingsBulkUpdate( $hash, "aktActionFinish", "0" ) ; readingsBulkUpdate( $hash, "LastAction", $comand ); readingsEndUpdate( $hash, 1); if ($comand eq "off") { readingsSingleUpdate( $hash, "state", "runningUp" , 1 ) ; # internen timer setzen runningtime - dann states setzen Log3( $name, 5, "Siro-Set: setze timer -$comand"); InternalTimer( $endaction, "FHEM::Siro::Finish", "$name" ); } else { readingsSingleUpdate( $hash, "state", "runningUpfortimer" , 1 ) ; } #befehl ausfuhren } ############################# # set level ( positionsanfahrt ) if ($comand eq "level") { if ( AttrVal($name,'SIRO_inversPosition','0') eq '1' ) { $zielposition = 100 - $zielposition; $state = 100 - $state; } Log3( $name, 3, "Siro-Set ($name) : set Position $zielposition "); if ($downtime eq "undef" || $uptime eq "undef") # bei ungesetzten fahrzeiten { Log3( $name, 1, "ERROR Siro - Set: Function is not available without set runtime attribute, please define"); readingsSingleUpdate( $hash, "LastAction", $comand, 1 ); readingsSingleUpdate( $hash, "motor-term", "Function is not available without set runtime attribute, please define", 1 ) ; return "Function PCT is not available without set runtime attribute, please define "; } my $timetodrive; #enth?tlt fahrzeit my $cmdpos ="undef"; # enth?lt fahrbefehl f?r gew?nschte richtung my $cmdactiontime ; # enth?lt fahrtdauer f?r gew?nschte position my $directionmsg; #enth?lt actionstesxt # geforderte farhtrichtung ermitteln if ($state < $zielposition) # fahrt runter ben?tigt { $cmdpos = "on"; # fahrdauer ermitteln $timetodrive = ($zielposition - $state) * $down1time; $directionmsg ="runningDown"; } if ($state > $zielposition) # fahrt hoch ben?tigt { $cmdpos = "off"; # fahrdauer ermitteln $timetodrive = ($state - $zielposition) * $up1time; $directionmsg ="runningUp"; } my $endaction = time + $timetodrive; SendCommand( $hash, $cmdpos ); readingsBeginUpdate($hash); readingsBulkUpdate( $hash, "aktRunningAction", 'position' ) ; readingsBulkUpdate( $hash, "aktEndAction", $endaction ) ; readingsBulkUpdate( $hash, "aktTimeAction", $timetodrive ) ; readingsBulkUpdate( $hash, "aktActionFinish", $zielposition ) ; readingsBulkUpdate( $hash, "state", $directionmsg ) ; readingsEndUpdate( $hash, 1); # internen timer setzen runningtime - dann states setzen Log3( $name, 5, "Siro-Set: setze timer -$comand"); InternalTimer( $endaction, "FHEM::Siro::Finish", "$name" ); Log3( $name, 5, "Siro-Set: found direction - $cmdpos"); Log3( $name, 5, "Siro-Set: found finish - $zielposition"); Log3( $name, 5, "Siro-Set: found position now - $state"); } ############################# # set stop if ($comand eq "stop" && ReadingsVal( $name, 'LastAction', 'undef' ) ne $comand ) { Log3( $name, 3, "Siro-Set ($name) : set Stop "); if ($downtime eq "undef" || $uptime eq "undef") # bei ungesetzten fahrzeiten { SendCommand( $hash, 'stop' ); readingsBeginUpdate($hash); readingsBulkUpdate( $hash, "state", "notAvaible" ) ; readingsBulkUpdate( $hash, "LastAction", $comand ); readingsBulkUpdate( $hash, "motor-term", "Function is not available without set runtime attribute, please define", 1 ) ; readingsEndUpdate( $hash, 1); return; } else # bei gesetzten fahrzeiten { SendCommand( $hash, 'stop' ); } } ############################# # batteriecheck if ( AttrVal( $name, 'SIRO_Battery_low','undef' ) ne "undef") { readingsSingleUpdate( $hash, "batteryState", "ok" , 1 ) if (AttrVal( $name, 'SIRO_Battery_low','' ) > ReadingsNum( $name, 'motor-term', 0 )); readingsSingleUpdate( $hash, "batteryState", "low" , 1 ) if (AttrVal( $name, 'SIRO_Battery_low','' ) < ReadingsNum( $name, 'motor-term', 0 )); } else { readingsSingleUpdate( $hash, "batteryState", "unknown" , 1 ); } return; } ############################# sub Delock($) { # entsperrt device nach programmierung des shutters my ($input) = @_; my ( $name, $arg ) = split(/ /, $input ); my $hash = $defs{$name}; my $position = ReadingsVal( $name, 'pct', '' ); #position pct bis zum ende einer aktion readingsSingleUpdate( $hash, "state", $position , 1 ); } ############################# sub Prog($) { #wird im programmiermode von internaltimer aufgerufen my ($input) = @_; my ( $name, $arg ) = split(/ /, $input ); my $hash = $defs{$name}; Log3( $name, 5, "Siro-Prog: $arg "); SendCommand( $hash, $arg ); return; } ############################# sub Finish($) { # wird bei errechnetem aktionsende aufgerufen my ($input) = @_; my ( $name, $arg ) = split( / /, $input ); my $hash = $defs{$name}; return "" if ( IsDisabled($name) ); my $invers = 1; my $action = ReadingsVal( $name, 'aktRunningAction', '' ); my $state = ReadingsVal( $name, 'aktActionFinish', 'notAvaible' ); my $operationtime = ReadingsNum( $name, 'motor-term', 0 ); my $newoperationtime = $operationtime + ReadingsNum ($name, 'aktTimeAction', 0 ); Log3( $name, 5, "Siro-Finish: action - $action"); SendCommand( $hash, 'stop' ) if ( $action ne "on" && $action ne "off" ) ; if ( AttrVal($name,'SIRO_inversPosition','0') eq '1' ) { $state = 100 - $state; } readingsBeginUpdate($hash); readingsBulkUpdate( $hash, "state", $state ) ; readingsBulkUpdate( $hash, "pct", $state ) ; readingsBulkUpdate( $hash, "position", $state ) ; readingsBulkUpdate( $hash, "aktRunningAction", "noAction" ) ; readingsBulkUpdate( $hash, "aktEndAction", 0 ) ; readingsBulkUpdate( $hash, "aktTimeAction", 0 ) ; readingsBulkUpdate( $hash, "aktActionFinish", 0 ) ; readingsBulkUpdate( $hash, "motor-term", $newoperationtime, 1 ) ; readingsEndUpdate( $hash, 1); return; } ############################# sub Restartset($) { my ($input) = @_; my ( $name, $arg ) = split( / /, $input ); my $hash = $defs{$name}; return "" if ( IsDisabled($name) ); Log3( $name, 5, "Siro-Restartset : aufgerufen"); my $cmd = $hash->{helper}{savedcmds}{cmd1}; my $pos = $hash->{helper}{savedcmds}{cmd2}; delete( $hash->{helper}{savedcmds} ); Log3( $name, 5, "Siro-Restartset : cmds $input"); Set($hash, $name, $cmd , $pos); return; } ############################# sub fhemwebFn($$$$) { my ( $FW_wname, $d, $room, $pageHash ) =@_; # pageHash is set for summaryFn. my $hash = $defs{$d}; my $name = $hash->{NAME}; my $progmode =$hash->{helper}{progmode}; Log3( $name, 5, "Siro-progmode: reached progmode $progmode"); if (!defined $progmode){$progmode='off';} my $msg; ############################# # debugmode if (AttrVal( $name, 'SIRO_no_IO_msg',0 ) eq "1") { delete( $hash->{Signalduino_RAWMSG} ); delete( $hash->{Signalduino_MSGCNT} ); delete( $hash->{Signalduino_RSSI} ); delete( $hash->{Signalduino_TIME} ); } if (AttrVal( $name, 'SIRO_debug', "0" ) eq "1") { $msg.= "
 
Das Device ist im Debugmodus, es werden keine Befehle gesendet"; $msg.= "
 
"; } ############################# if (AttrVal( $name, 'disable', "0" ) eq "1") { $msg= "
 
Das Device ist disabled"; $msg.= "
 
"; } if ( $progmode eq "on") { $msg= "
 
Programmiermodus aktiv, es werden nur folgende Befehle unterstuetzt:
 
"; $msg.= "Das Anlernen ene Rollos erfolgt unter der ID: "; my $sendid = AttrVal( $name, 'SIRO_send_id', 'undef' ); if ( $sendid eq 'undef') { $msg.= $hash->{ID} ; } else { $msg.= $sendid ; } $msg.= " und dem Kanal: "; my $sendchan = AttrVal( $name, 'SIRO_send_channel', 'undef' ); if ( $sendchan eq 'undef') { $msg.= $hash->{CHANNEL_RECEIVE} ; } else { $msg.= $sendchan ; } $msg.= "
 
"; $msg.= ""; $msg.= " "; $msg.= ""; $msg.= " "; $msg.= ""; $msg.= " "; $msg.= ""; $msg.= " "; $msg.= ""; $msg.= " "; $msg.= " "; $msg.= " "; $msg.= " "; $msg.= " "; $msg.= " "; $msg.= " "; $msg.= " "; $msg.= " "; $msg.= " "; $msg.= ""; $msg.= " 
 "; $msg.= "
- Motor anlernen: P2,P2,DOWN je nach Wicklung des Rollos"; $msg.= " "; $msg.= "
- Motor anlernen: P2,P2,UP je nach Wicklung des Rollos"; $msg.= " "; $msg.= "
- Einstellmodus aktivieren: P2, UP, P2"; $msg.= " "; $msg.= "
- Endlagen loeschen: P2, DOWN, P2"; $msg.= " "; $msg.= "
- Pairing loeschen: P2, STOP, P2"; $msg.= " "; $msg.= "
 
"; } $msg.= ""; return $msg; } ############################# sub Siro_icon(@) { my ($name,$icon) = @_; my $hash = $defs{$name}; my $state = ReadingsVal( $name, 'state', 'undef' ); my $move ="stop"; $move = "open" if $state eq "100"; $move = "close" if $state eq "0"; if ($state =~ m/[a-z].*/){$state=0;} my $sticon = "fts_shutter_1w_"; $sticon = $icon if defined $icon; my $invers = AttrVal( $name, 'SIRO_inversPosition',0 ); my $ret ="programming:edit_settings notAvaible:hue_room_garage runningUp.*:fts_shutter_up:stop runningDown.*:fts_shutter_down:stop ".$state.":".$sticon.(int($state/10)*10).":".$move; $ret ="programming:edit_settings notAvaible:hue_room_garage runningUp.*:fts_shutter_up:stop runningDown.*:fts_shutter_down:stop ".$state.":".$sticon.(100 - (int($state/10)*10)).":".$move if $invers eq "1"; $ret =".*:fts_shutter_all" if ($hash->{CHANNEL_RECEIVE} eq '0'); $ret =".*:secur_locked\@red" if ReadingsVal( $name, 'lock_cmd', 'off' ) eq 'on'; return $ret; } ############################# sub Distributor($) { my ($input) = @_; my ( $name, $arg, $cmd ) = split( / /, $input ); my $hash = $defs{$name}; return "" if ( IsDisabled($name) ); Log3( $name, 5, "Siro-Distributor : aufgerufen"); Log3( $name, 5, "Siro-Distributor : Befehl - $arg"); #suche devices my $devspec="TYPE=Siro:FILTER=ID=".$hash->{ID}.".*"; my @devicelist = devspec2array($devspec); shift @devicelist; my $devicelist = join(" ",@devicelist); my $owndef = $hash->{ID}; Log3( $name, 5, "Siro-Distributor : own DEF - ".$owndef); my @list =qw( 1 2 3 4 5 6 7 8 9 A B C D E F); foreach my $key (@list) { my $targdev = $owndef.$key; if (defined $modules{Siro}{defptr}{$targdev}) { Log3( $name, 5, "Siro-Distributor : found defice kanal $key ID ".$modules{Siro}{defptr}{$targdev}); my $devhash = $modules{Siro}{defptr}{$targdev}; my $msg = "P72#".$targdev.$cmd; my $devname = $devhash->{NAME}; #$cmd = $codes{$cmd}; Log3( $name, 5, "Siro-Distributor : transfer msg für $devname - $msg -$cmd"); fhem( "set " . $devname . " " . $arg. " fakeremote" ); } } readingsSingleUpdate( $hash, "LastAction", $arg, 1 ); readingsSingleUpdate( $hash, "state", $arg, 1 ); readingsSingleUpdate( $hash, "GroupDevices", $devicelist, 1 ); delete( $hash->{Signalduino_RAWMSG} ); delete( $hash->{Signalduino_MSGCNT} ); delete( $hash->{Signalduino_RSSI} ); delete( $hash->{Signalduino_TIME} ); return; } 1; =pod =item summary Supports rf shutters from Siro =item summary_DE Unterstützt Siro Rollo-Funkmotoren =begin html

Siro protocol

=end html =begin html_DE

Siro protocol

=end html_DE =cut