# $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" #oldversion entfernen mit kommender version # . " SIRO_channel:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15" . " SignalRepeats:1,2,3,4,5,6,7,8,9" . " SignalLongStopRepeats:10,15,20,40,45,50" . " channel_send_mode_1:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15" . " $readingFnAttributes" . " setList" . " ignore:0,1" . " dummy:1,0" . " time_to_open" . " time_to_close" . " time_down_to_favorite" . " hash" . " operation_mode:0,1" . " debug_mode:0,1" . " down_limit_mode_1:slider,0,1,100" . " down_auto_stop:slider,0,1,100" . " invers_position:0,1" . " prog_fav_sequence"; $hash->{AutoCreate} = { "Siro.*" => { #ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", autocreateThreshold => "2:10" } }; #$hash->{NOTIFYDEV} = "global"; $hash->{helper}{progmode} = "off"; #exexcmd on #$hash->{helper}{exexcmd} = "on"; #FHEM::Siro::LoadHelper($hash) if ($init_done); } ############################# #### 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", "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", "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", "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) 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; } Log3( $name, 4,"Siro_sendCommand: args1 - $args[1]") if defined $args[1]; #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 if ( $cmd eq 'changeconfig'){ versionchange( $name ); return; } # 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"; 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") { 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 versionchange($) { my ($input) = @_; my ( $name, $arg ) = split( / /, $input ); my $hash = $defs{$name}; return "" if ( IsDisabled($name) ); Log3( $name, 0, "Siro - versionchange : aufruf"); my $attr; $attr = AttrVal($name,'time_to_close','undef'); CommandAttr(undef,$name . ' SIRO_time_to_close ' . $attr) if ( AttrVal($name,'time_to_close','undef') ne 'undef' ); fhem("deleteattr $name time_to_close"); $attr = AttrVal($name,'time_to_open','undef'); CommandAttr(undef,$name . ' SIRO_time_to_open ' . $attr) if ( AttrVal($name,'time_to_open','undef') ne 'undef' ); fhem("deleteattr $name time_to_open"); $attr = AttrVal($name,'SignalLongStopRepeats','undef'); CommandAttr(undef,$name . ' SIRO_signalLongStopRepeats ' . $attr) if ( AttrVal($name,'SignalLongStopRepeats','undef') ne 'undef' ); fhem("deleteattr $name SignalLongStopRepeats"); $attr = AttrVal($name,'SignalRepeats','undef'); CommandAttr(undef,$name . ' SIRO_signalRepeats ' . $attr) if ( AttrVal($name,'SignalRepeats','undef') ne 'undef' ); fhem("deleteattr $name SignalRepeats"); $attr = AttrVal($name,'invers_position','undef'); CommandAttr(undef,$name . ' SIRO_inversPosition ' . $attr) if ( AttrVal($name,'invers_position','undef') ne 'undef' ); fhem("deleteattr $name invers_position"); CommandAttr( undef,$name . ' devStateIcon {return FHEM::Siro::Siro_icon($name)}' ); CommandAttr(undef,$name . ' webCmd stop:open:close:fav:pct'); $attr = AttrVal($name,'operation_mode','undef'); if ($attr eq "1"){ my $modch = AttrVal($name,'channel_send_mode_1','undef'); CommandAttr(undef,$name . ' SIRO_send_channel ' . $modch) } fhem("deleteattr $name operation_mode"); fhem("deleteattr $name channel_send_mode_1"); fhem("deleteattr $name down_limit_mode_1"); fhem("deleteattr $name operation_mode"); fhem("deleteattr $name invers_position"); fhem("deleteattr $name down_auto_stop"); fhem("deleteattr $name prog_fav_sequence"); fhem("deleteattr $name time_down_to_favorite"); fhem("deleteattr $name time_down_to_favorite"); my $seconds = ReadingsVal( $name, 'operating_seconds', '0' ); fhem("deletereading $name .*"); readingsBeginUpdate($hash); readingsBulkUpdate( $hash, "state", "0" ); readingsBulkUpdate( $hash, "pct", "0" ) ; readingsBulkUpdate( $hash, "position", "0" ) ; readingsBulkUpdate( $hash, "motor-term", $seconds ) ; readingsEndUpdate( $hash, 1 ); SendCommand( $hash, 'off' ); 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.= "
 
"; } ############################# versionsänderung # kann irgendwann entfernt werden if (ReadingsVal( $name, 'last_reset_os', 'undef' ) ne 'undef') { $msg.= "
 
ACHTUNG !
 
Das Siromudul wurde komplett erneuert.
Die vorhandenen Attribute und Readings sind inkompatibel und das Device derzeit nur bedingt funktionsfaehig:
 
Durch druecken des untenstehenden Buttons ist eine automatisch Neukonfiguration moeglich, dabei werden vorhandene Daten beruecksichtigt. Nach betaetigen des Buttons macht das Rollo eine Initialisierungsfahrt nach oben.
 
Danach ist eine Funktion mit der alten Siroversion nicht mehr moeglich.
Fuer den Fall, das doch die alte Version wieder eingesetzt werden sollte ist die jetzt vorhandene Rawdefinition vor einer Umstellung zu sichern.
Wichtig: Bei einer automatischen Umstellung werden entgegen den Massgaben vorhandene Userattribute geaendert ! "; $msg.= "
 
"; $msg.= ""; $msg.= " "; $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.= "
- 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