98_Siro.pm 95 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435
  1. # $Id: 98_Siro.pm 16472 2018-03-23 15:03:57Z Byte09 $
  2. #
  3. # Siro module for FHEM
  4. # Thanks for templates/coding from SIGNALduino team and Jarnsen_darkmission_ralf9
  5. #
  6. # Needs SIGNALduino >= V3.3.1-dev (10.03.2017).
  7. # Published under GNU GPL License, v2
  8. # History:
  9. # 0.01 2017-05-24 Smag Decoding/sniffing signals, Binary-keycode-decoding for on, off, stop, favourite and p2 (pairing).
  10. # 0.02 2017-05-25 Smag Tests with CUL/COC and Signalduino. Successful signalrepeat to device via Signalduino.
  11. # 0.03 2017-07-23 Smag initial template
  12. # 0.04 2017-07-24 Smag Changed binary device-define to much more easier hex-code (28bit+channel).
  13. # 0.10 2017-07-30 Smag Siro-Parse implemented. First alpha-version went out for Byte09 and det
  14. # 0.11 2017-07-30 Smag, Byte09 updated module
  15. # 0.12 2017-08-02 Byte Subroutine X_internaltset komplett entfernt, Zusammenlegung mit X_set
  16. # Byte Variablen bereinigt
  17. # Byte Code bereinigt
  18. # Byte Einführung "readings", "lastmsg" und "aktMsg" inkl. Unixtime
  19. # 0.13 2017-08-03 Byte Senden eines doppelten Stoppbefehls abgefangen, keine Hardwaremittelanfahrt durch zweimaliges Stopp mehr möglich
  20. # 0.14 2017-08-04 Byte Attr SignalLongStopRepeats eingeführt. Anzahl der Repeats für Longstop (Favourite). undef = 15
  21. # 0.15 2017-08-05 Byte Attr "timer" eingeführt. Enthält die Zeit zwischen zwei ausgeführten Befehlen zur Positionsberechnung
  22. # 0.16 2017-08-06 Byte Berechnung der aktuellen Position nach Stop-Kommando - Darstellung im state bzw. im Slider
  23. # 0.17 2017-08-09 Byte Fehler bei der Positionsfahrt aus der Fav-Position behoben
  24. # 0.18 2017-08-10 Byte Änderung des State bei Empfang von FB integriert
  25. # 0.19 2017-08-12 Byte Attr 'position_adjust' abgeschafft
  26. # 0.20 2017-08-12 Attr 'operation_mode' eingeführt
  27. # 0 Normaler Modus : Keine 'position_adjust' - Fahrt über 0
  28. # 1 Normaler Modus : Positionsanfahrt immer -> 'position_adjust' - Fahrt über 0
  29. # 2 Repeater Modus : Modul empfängt die FB und leitet es an den Motor weiter. Motor empfängt die FB nicht direkt.
  30. # Kanalanpassung notwendig. Zuverlässigster Betrieb!
  31. # 0.21 2017-08-13 Smag: Code-Cleanup. Spellcheck. Documentation
  32. # 0.22 2017-08-18 Byte: Kompletten Code überarbeitet. Laufzeitoptimierung. Reaktionszeit verbessert. Unnötige Readings in die Internals verlagert. Fehler bei direktem umschalten behoben.
  33. # Operation_mode 1 komplett entfernt, om 2 ist nun om 1. Operationmode 1 bis Fertigstellung deaktiviert
  34. # 0.23 Beta Byte V0.22 - > V1.0 Beta
  35. # 0.24 2017-08-20 Byte Positionsanfahrt über Alexa möglich - "schalte DEVICE XX%"
  36. # Operation_mode 1 eingeführt. ( Repeatermodus )
  37. # 0.25 2017-08-26 Byte diverse Korrekturen, Codebereinigung, Anfahrt der HW_Favorit Position über FB im Mode 1 möglich
  38. # 0.26 2017-08-26 Byte Commandref Deutsch hinzugefügt
  39. # 0.27 2017-08-29 Byte Define von Devices, die eine Kanal nutzen der bereits von einem Device genutzt wird (channel / send_channel_mode1) wird unterbunden
  40. # Debug_Attr (0/1) eingefügt - es werden diverse redings angelegt und kein Befehl physisch anden Rollo gesendet - nur Fehlersuche
  41. # 0.28 2017-09-02 ByteFehler bei Stateaktualisierung in Zusammenhang mit Stop bei Favoritenanfahrt behoben
  42. # 0.29 2017-08-29 Byte Define von Devices, die einen Kanal nutzen, der bereits von einem Device genutzt wird (channel / send_channel_mode1) wird unterbunden
  43. # Debug_Attr (0/1) eingefügt - es werden diverse redings angelegt und kein Befehl physisch anden Rollo gesendet - nur Fehlersuche
  44. # Set favorite und Attr prog_fav_sequence eingeführt - programmierung derHardware_Favorite_Position
  45. # Codebereinigung
  46. # Allgemeine Fehler behoben
  47. # 0.30 2017-09-09 Byte Betrieb in eingeschränkter Funktion ohne 'time'- Attribute möglich
  48. # 0.31 2017-09-10 Byte Commandref ergänzt Deutsch/Englisch
  49. # 0.32 2017-09-16 Byte Fehlerkorrekturen
  50. # 0.34 2017-09-17 Invers Dokumentation, Byte Korrekturen Log
  51. # 0.35 2017-09-24 Byte Fehlerkorrekturen , Einbau Device mit Kanal 0 als Gruppendevice ( noch gesperrt ) . Attribut "channel" enfernt , Kanalwahl nur noch über das Device möglich .
  52. # 0.36 2017-09-24 Byte Device0 Favoritenanfahrt und Positionsanfahrt durch FHEM möglich
  53. # 0.37 2017-09-25 SMag Prerelease-Vorbereitungen. Codeformatierung, Fehlerkorrekturen, Textkorrekturen.
  54. # 0.38 2017-09-27 optimierung sub Siro_Setgroup($) -> laufzeitverbesserung
  55. #
  56. # 0.39 2017-10-14 Byte Log überarbeitet / Parse überarbeitet / Define überarbeitet / interne Datenstruktur geändert / Internals überarbeitet / Groupdevice ( Kanal 0 ) möglich . Fehlerkorrekturen / attribut down_for_timer und up_for_timer eingebaut
  57. # 0.40 2017-10-15 Byte Code bereinigt
  58. # 0.41 2017-10-17 Byte anpassung der %Sets je nach device ( groupdevice )
  59. # 0.42 2017-10-18 Byte attr "down_auto_stop" eingefügt - beendet runterfahrt durch on/close/fb bei ATTR weiterfahrt durch nochmalige cmd . Comandref ergänzt
  60. # 0.43 2017-10-19 Byte attr "invers_position[0/1]" eingefügt. Invertiert positionsanzeige und anfahrt 0 -> 100% = rollo geschlossen - 1 -> 0% =rollo geschlossen
  61. # 0.44 2017-10-19 Byte bugfix -> set favorite. Unterscheidung ob "time_down_to_favorite" gesetzt oder nicht. ( interpretation :favorite programmiert oder nicht ) - entsprechende anpassung des kommandos ( erst löschen -> dann speichern )
  62. # 0.45 2017-10-28 Byte fehler bei erneutem Kommando während Positionsanfahrten behoben
  63. # 0.46 2017-05-11 Byte fehler bei fhem-neustart behoben
  64. # 0.47 2017-11-11 Byte attr Disable zugefügt.
  65. # 0.47 2017-02-12 Byte/Dr Smag Quickfix ERB15LE
  66. # 0.48 2018-26-01 Byte New Readings 'operating_seconds' / 'last_reset_os'
  67. # 0.49 2018-03-03 Byte Fix for Signalduino V 3.3.1-RC2 SIGNALduino cc1101
  68. ################################################################################################################
  69. # Todo's:
  70. # -
  71. # -
  72. ###############################################################################################################
  73. package main;
  74. use strict;
  75. use warnings;
  76. my $version = "V 0.49";
  77. my %codes = (
  78. "55" => "stop", # Stop the current movement or move to custom position
  79. "11" => "off", # Move "up"
  80. "33" => "on", # Move "down"
  81. "CC" => "prog", # Programming-Mode (Remote-control-key: P2)
  82. );
  83. my %sets = (
  84. "open" => "noArg",
  85. "close" => "noArg",
  86. "off" => "noArg",
  87. "stop" => "noArg",
  88. "on" => "noArg",
  89. "fav" => "noArg",
  90. "prog" => "noArg",
  91. "prog_stop" => "noArg",
  92. "position" =>
  93. "slider,0,1,100", # Wird nur bei vorhandenen time_to attributen gesetzt
  94. "state" => "noArg",
  95. "set_favorite" => "noArg",
  96. "down_for_timer" => "textField",
  97. "reset_operating_seconds" => "noArg",
  98. "up_for_timer" => "textField",
  99. );
  100. my %sendCommands = (
  101. "off" => "off",
  102. "stop" => "stop",
  103. "on" => "on",
  104. "open" => "off",
  105. "close" => "on",
  106. "fav" => "fav",
  107. "prog" => "prog",
  108. "set_favorite" => "setfav"
  109. );
  110. my %siro_c2b;
  111. my %models = (
  112. siroblinds => 'blinds',
  113. siroshutter => 'shutter'
  114. );
  115. #################################################################
  116. sub Siro_Initialize($) {
  117. my ($hash) = @_;
  118. # Map commands from web interface to codes used in Siro
  119. foreach my $k ( keys %codes ) {
  120. $siro_c2b{ $codes{$k} } = $k;
  121. }
  122. $hash->{SetFn} = "Siro_Set";
  123. $hash->{NotifyFn} = "Siro_Notify";
  124. $hash->{ShutdownFn} = "Siro_Shutdown";
  125. #$hash->{StateFn} = "Siro_SetState"; #change
  126. $hash->{DefFn} = "Siro_Define";
  127. $hash->{UndefFn} = "Siro_Undef";
  128. $hash->{DeleteFn} = "Siro_Delete";
  129. $hash->{ParseFn} = "Siro_Parse";
  130. $hash->{AttrFn} = "Siro_Attr";
  131. $hash->{Match} = "^P72#[A-Fa-f0-9]+";
  132. $hash->{AttrList} =
  133. " IODev"
  134. . " disable:0,1"
  135. . " SignalRepeats:1,2,3,4,5,6,7,8,9"
  136. . " SignalLongStopRepeats:10,15,20,40,45,50"
  137. . " channel_send_mode_1:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15"
  138. . " $readingFnAttributes"
  139. . " setList"
  140. . " ignore:0,1"
  141. . " dummy:1,0"
  142. # . " model:siroblinds,siroshutter"
  143. . " time_to_open"
  144. . " time_to_close"
  145. . " time_down_to_favorite" . " hash"
  146. . " operation_mode:0,1"
  147. . " debug_mode:0,1"
  148. . " down_limit_mode_1:slider,0,1,100"
  149. . " down_auto_stop:slider,0,1,100"
  150. . " invers_position:0,1"
  151. . " prog_fav_sequence";
  152. $hash->{AutoCreate} = {
  153. "Siro.*" => {
  154. ATTR => "event-min-interval:.*:300 event-on-change-reading:.*",
  155. FILTER => "%NAME",
  156. autocreateThreshold => "2:10"
  157. }
  158. };
  159. $hash->{NOTIFYDEV} = "global";
  160. Siro_LoadHelper($hash) if ($init_done);
  161. }
  162. #################################################################
  163. sub Siro_Define($$) {
  164. my ( $hash, $def ) = @_;
  165. my @a = split( "[ \t][ \t]*", $def );
  166. my $u = "Wrong syntax: define <name> Siro id ";
  167. my $askedchannel; # Angefragter kanal
  168. # Fail early and display syntax help
  169. if ( int(@a) < 3 ) {
  170. return $u;
  171. }
  172. if ( $a[2] =~ m/^[A-Fa-f0-9]{8}$/i ) {
  173. $hash->{ID} = uc( substr( $a[2], 0, 7 ) );
  174. $hash->{CHANNEL} = sprintf( "%d", hex( substr( $a[2], 7, 1 ) ) );
  175. $askedchannel = sprintf( "%d", hex( substr( $a[2], 7, 1 ) ) );
  176. }
  177. else {
  178. return
  179. "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. ";
  180. }
  181. my $test;
  182. my $device;
  183. my $chanm1;
  184. my $chan;
  185. my @channels = ( '', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' );
  186. my @testchannels; # Enthält alle Kanäle die in Benutzung sind
  187. my @keys;
  188. my @values;
  189. my $testname;
  190. $hash->{Version} = $version;
  191. my $name = $a[0];
  192. # Log3($name, 0, "Siro_define: $hash->{helper}{position} ");
  193. # if ($hash->{helper}{position} eq '')
  194. # {
  195. # $hash->{helper}{position}="0";
  196. # $hash->{helper}{aktMsg} = "stop".' '.'0'.' '.gettimeofday();
  197. # $hash->{helper}{lastMsg} = "stop".' '.'0'.' '.gettimeofday();
  198. # $hash->{helper}{lastProg} ="0";
  199. # $hash->{helper}{lastparse_stop} ="stop".' '.gettimeofday();
  200. # $hash->{helper}{lastparse} ="";
  201. # $hash->{helper}{parse_aborted} ="0";
  202. # }
  203. my $tn = TimeNow(); #Wird wohl nicht benötigt?!?! down_limit_mode_1
  204. if ( $askedchannel ne "0" ) {
  205. #Setzen der vordefinierten Attribute
  206. $attr{$name}{devStateIcon} =
  207. "{return '.*:fts_shutter_1w_'.(int(\x{24}state/10)*10)}"
  208. if ( !defined( $attr{$name}{devStateIcon} ) ); #TMP_Byte09 !defined
  209. $attr{$name}{webCmd} = "stop:on:off:fav:position"
  210. if ( !defined( $attr{$name}{webCmd} ) );
  211. $attr{$name}{down_limit_mode_1} = "100"
  212. if ( !defined( $attr{$name}{down_limit_mode_1} ) );
  213. $attr{$name}{prog_fav_sequence} = "prog,2,stop,2,stop"
  214. if ( !defined( $attr{$name}{prog_fav_sequence} ) );
  215. #$attr{$name}{room} ="Siro" if (!defined ($attr{$name}{genericDeviceType}));
  216. $attr{$name}{SignalLongStopRepeats} = "15"
  217. if ( !defined( $attr{$name}{SignalLongStopRepeats} ) );
  218. $attr{$name}{SignalRepeats} = "8"
  219. if ( !defined( $attr{$name}{SignalRepeats} ) );
  220. $attr{$name}{operation_mode} = "0"
  221. if ( !defined( $attr{$name}{operation_mode} ) );
  222. $attr{$name}{down_auto_stop} = "100"
  223. if ( !defined( $attr{$name}{down_auto_stop} ) );
  224. $attr{$name}{invers_position} = "0"
  225. if ( !defined( $attr{$name}{invers_position} ) );
  226. }
  227. else {
  228. $attr{$name}{devStateIcon} =
  229. "{return '.*:fts_shutter_1w_'.(int(\x{24}state/10)*10)}"
  230. if ( !defined( $attr{$name}{devStateIcon} ) ); #TMP_Byte09 !defined
  231. $attr{$name}{webCmd} = "stop:on:off:fav:position"
  232. if ( !defined( $attr{$name}{webCmd} ) );
  233. #$attr{$name}{room} ="Siro" if (!defined ($attr{$name}{genericDeviceType}));
  234. $attr{$name}{SignalLongStopRepeats} = "15"
  235. if ( !defined( $attr{$name}{SignalLongStopRepeats} ) );
  236. $attr{$name}{SignalRepeats} = "8"
  237. if ( !defined( $attr{$name}{SignalRepeats} ) );
  238. #$attr{$name}{SignalRepeats} ="2";
  239. $attr{$name}{down_auto_stop} = "100"
  240. if ( !defined( $attr{$name}{down_auto_stop} ) );
  241. $attr{$name}{invers_position} = "0"
  242. if ( !defined( $attr{$name}{invers_position} ) );
  243. }
  244. my $code = uc( $a[2] );
  245. my $ncode = 1;
  246. my $devpointer = $hash->{ID} . $hash->{CHANNEL};
  247. $hash->{CODE}{ $ncode++ } = $code;
  248. $modules{Siro}{defptr}{$devpointer} = $hash;
  249. Log3( $name, 5,
  250. "Siro_define: angelegtes Device - code -> $code name -> $name hash -> $hash "
  251. );
  252. AssignIoPort($hash);
  253. }
  254. #################################################################
  255. sub Siro_Undef($$) {
  256. my ( $hash, $name ) = @_;
  257. delete( $modules{Siro}{defptr}{$hash} );
  258. return undef;
  259. }
  260. #################################################################
  261. sub Siro_Shutdown($) {
  262. my ($hash) = @_;
  263. my $name = $hash->{NAME};
  264. readingsSingleUpdate( $hash, ".aktMsg", $hash->{helper}{aktMsg}, 0 );
  265. readingsSingleUpdate( $hash, ".lastMsg", $hash->{helper}{lastMsg}, 0 );
  266. readingsSingleUpdate( $hash, ".lastProg", $hash->{helper}{lastProg}, 0 );
  267. readingsSingleUpdate( $hash, ".lastparse", $hash->{helper}{lastparse}, 0 );
  268. readingsSingleUpdate( $hash, ".lastparse_stop",
  269. $hash->{helper}{lastparse_stop}, 0 );
  270. readingsSingleUpdate( $hash, ".parse_aborted",
  271. $hash->{helper}{parse_aborted}, 0 );
  272. readingsSingleUpdate( $hash, ".positionsave", $hash->{helper}{position},
  273. 0 );
  274. readingsSingleUpdate( $hash, ".positiontimer",
  275. $hash->{helper}{positiontimer}, 0 );
  276. return;
  277. }
  278. #################################################################
  279. sub Siro_LoadHelper($) {
  280. my ($hash) = @_;
  281. my $name = $hash->{NAME};
  282. my $save = ReadingsVal( $name, '.aktMsg', 'undef' );
  283. if ( $save eq 'undef' ) {
  284. Log3( $name, 5, "Siro: Helper lade new device" );
  285. $hash->{helper}{position} = "0";
  286. $hash->{helper}{aktMsg} = "stop" . ' ' . '0' . ' ' . gettimeofday();
  287. $hash->{helper}{lastMsg} = "stop" . ' ' . '0' . ' ' . gettimeofday();
  288. $hash->{helper}{lastProg} = "0";
  289. $hash->{helper}{lastparse_stop} = "stop" . ' ' . gettimeofday();
  290. $hash->{helper}{lastparse} = "";
  291. $hash->{helper}{parse_aborted} = "0";
  292. }
  293. else {
  294. Log3( $name, 5, "Siro: Helper lade bestandsdaten" );
  295. $hash->{helper}{aktMsg} = ReadingsVal( $name, '.aktMsg', '' );
  296. $hash->{helper}{lastMsg} = ReadingsVal( $name, '.lastMsg', '' );
  297. $hash->{helper}{lastparse} = ReadingsVal( $name, '.lastparse', '' );
  298. $hash->{helper}{lastProg} = ReadingsVal( $name, '.lastProg', '' );
  299. $hash->{helper}{lastparse_stop} =
  300. ReadingsVal( $name, '.lastparse_stop', '' );
  301. $hash->{helper}{parse_aborted} =
  302. ReadingsVal( $name, '.parse_aborted', '' );
  303. $hash->{helper}{position} = ReadingsVal( $name, '.positionsave', '' );
  304. $hash->{helper}{positiontimer} =
  305. ReadingsVal( $name, '.positiontimer', '' );
  306. readingsSingleUpdate( $hash, "position",
  307. ReadingsVal( $name, '.positionsave', '0' ), 0 );
  308. readingsSingleUpdate( $hash, "state",
  309. ReadingsVal( $name, '.positionsave', '0' ), 0 );
  310. }
  311. Log3( $name, 5,
  312. "Siro: Helper initialisiert: positionsave "
  313. . ReadingsVal( $name, '.positionsave', '0' )
  314. . " " );
  315. Log3( $name, 5,
  316. "Siro: Helper initialisiert: state "
  317. . ReadingsVal( $name, 'state', '0' )
  318. . " " );
  319. Log3( $name, 5,
  320. "Siro: Helper initialisiert: position "
  321. . ReadingsVal( $name, 'position', '0' )
  322. . " " );
  323. return;
  324. }
  325. #################################################################
  326. sub Siro_Notify($$) {
  327. my ( $own_hash, $dev_hash ) = @_;
  328. my $ownName = $own_hash->{NAME}; # own name / hash
  329. my $devName = $dev_hash->{NAME}; # Device that created the events
  330. my $events = deviceEvents( $dev_hash, 1 );
  331. if ( $devName eq "global"
  332. && grep( m/^INITIALIZED|REREADCFG$/, @{$events} ) )
  333. {
  334. Siro_LoadHelper($own_hash);
  335. }
  336. return;
  337. }
  338. #################################################################
  339. sub Siro_Delete($$) {
  340. my ( $hash, $name ) = @_;
  341. return undef;
  342. }
  343. #################################################################
  344. sub Siro_SendCommand($@) {
  345. my ( $hash, @args ) = @_;
  346. my $ret = undef;
  347. my $cmd = $args[0]; # Command as text (on, off, stop, prog)
  348. my $message; # IO-Message (full)
  349. my $chan; # Channel
  350. my $binChannel; # Binary channel
  351. my $SignalRepeats; #
  352. my $name = $hash->{NAME};
  353. my $binHash;
  354. my $bin; # Full binary IO-Message
  355. my $binCommand;
  356. my $numberOfArgs = int(@args);
  357. my $command = $siro_c2b{$cmd};
  358. my $io = $hash->{IODev}; # IO-Device (SIGNALduino)
  359. if ( $cmd eq 'on' || $cmd eq 'off' ) {
  360. # setze startzeit
  361. $hash->{helper}{motorstart} = gettimeofday();
  362. #ReadingsVal( $name, '.positionsave', '' );
  363. }
  364. if ( $cmd eq 'stop'
  365. && gettimeofday() - $hash->{helper}{motorstart} <
  366. AttrVal( $name, 'time_to_open', 0 )
  367. && $hash->{CHANNEL} ne '0' )
  368. {
  369. my $addtime = gettimeofday() - $hash->{helper}{motorstart};
  370. my $oldmotortime = ReadingsVal( $name, 'operating_seconds', 0 );
  371. my $newtime = $oldmotortime + $addtime;
  372. my $rounded = int( 100 * $newtime + 0.5 ) / 100;
  373. readingsSingleUpdate( $hash, "operating_seconds", $rounded, 1 );
  374. }
  375. my $debug = AttrVal( $name, 'debug_mode', '0' );
  376. Log3( $name, 5,
  377. "Siro_sendCommand: hash -> $hash - $name -> cmd :$cmd: - args -> @args"
  378. );
  379. if ( $args[2] eq "longstop" ) {
  380. $SignalRepeats = AttrVal( $name, 'SignalLongStopRepeats', '15' );
  381. }
  382. else {
  383. $SignalRepeats = AttrVal( $name, 'SignalRepeats', '10' );
  384. }
  385. my $operationmode = AttrVal( $name, 'operation_mode', 'on' );
  386. Log3( $name, 5, "Siro_sendCommand: operationmode -> $operationmode" );
  387. if ( $operationmode eq '1' ) {
  388. $chan = AttrVal( $name, 'channel_send_mode_1', $hash->{CHANNEL} );
  389. Log3( $name, 5, "Siro_sendCommand: channel für OM1 -> $chan" );
  390. }
  391. else {
  392. $chan = AttrVal( $name, 'channel', undef );
  393. if ( !defined($chan) ) {
  394. $chan = $hash->{CHANNEL};
  395. }
  396. }
  397. if ( $chan eq "0" ) {
  398. Log3( $name, 5,
  399. "Siro_sendCommand: Aborted not sent on a channel 0 request " );
  400. return;
  401. }
  402. $binChannel = sprintf( "%04b", $chan );
  403. #Log3($name, 5, "Siro set channel: $chan ($binChannel) for $io->{NAME}");
  404. my $value = $name . " " . join( " ", @args );
  405. $binHash = sprintf( "%028b", hex( $hash->{ID} ) );
  406. Log3 $io, 5, "Siro_sendCommand: BinHash: = $binHash";
  407. $binCommand = sprintf( "%08b", hex($command) );
  408. Log3 $io, 5, "Siro_sendCommand: BinCommand: = $binCommand";
  409. $bin = $binHash . $binChannel . $binCommand; # Binary code to send
  410. Log3 $io, 5, "Siro_sendCommand: Siro set value = $value";
  411. $message = 'P72#' . $bin . '#R' . $SignalRepeats;
  412. if ( $debug eq "1" ) {
  413. readingsSingleUpdate( $hash, "DEBUG_SEND",
  414. "$name -> message :$message: ", 1 );
  415. }
  416. else {
  417. Log3 $io, 5,
  418. "Siro_sendCommand: Siro_sendCommand: $name -> message :$message: ";
  419. if ( $args[2] eq "longstop" && $SignalRepeats > 35)
  420. {
  421. my $newsignalrepeats = int($SignalRepeats/3);
  422. Log3( $name, 5,"Siro_sendCommand: found oversized Longstop -> newlongstop $newsignalrepeats * 15 " );
  423. my $testrepeats = $newsignalrepeats*3;
  424. my $lastrepeats = $SignalRepeats-$testrepeats;
  425. Log3( $name, 5,"Siro_sendCommand: found oversized Longstop -> Block 1-3 repeats 15 " );
  426. Log3( $name, 5,"Siro_sendCommand: found oversized Longstop -> Block 4 repeats $lastrepeats " );
  427. $message = 'P72#' . $bin . '#R' . $newsignalrepeats;
  428. IOWrite( $hash, 'sendMsg', $message );
  429. Log3 $io, 5,"Siro_sendCommand: Siro_sendCommand: Block1 ";
  430. Log3( $name, 5,"Siro_sendCommand: found oversized Longstop -> send Block 1 " );
  431. IOWrite( $hash, 'sendMsg', $message );
  432. Log3 $io, 5,"Siro_sendCommand: Siro_sendCommand: Block2 ";
  433. Log3( $name, 5,"Siro_sendCommand: found oversized Longstop -> send Block 2 " );
  434. IOWrite( $hash, 'sendMsg', $message );
  435. Log3 $io, 5,"Siro_sendCommand: Siro_sendCommand: Block3 ";
  436. Log3( $name, 5,"Siro_sendCommand: found oversized Longstop -> send Block 3 " );
  437. if ($lastrepeats > 0)
  438. {
  439. $message = 'P72#' . $bin . '#R' . $lastrepeats;
  440. IOWrite( $hash, 'sendMsg', $message );
  441. Log3 $io, 5,"Siro_sendCommand: Siro_sendCommand: Block3 ";
  442. Log3( $name, 5,"Siro_sendCommand: found oversized Longstop -> send Block 4 " );
  443. }
  444. }
  445. else
  446. {
  447. IOWrite( $hash, 'sendMsg', $message );
  448. }
  449. }
  450. my $devicename = $hash->{IODev};
  451. Log3( $name, 2,
  452. "Siro_sendCommand: name -> $name command -> $cmd channel -> $chan bincmd -> $binCommand "
  453. );
  454. #Log3($name, 3, "Siro_sendCommand: execute comand $cmd - sendMsg to $devicename channel $chan -> $message ");
  455. return $ret;
  456. }
  457. #################################################################
  458. sub Siro_Parse($$) {
  459. my $debug = '';
  460. my @args;
  461. my ( $hash, $msg ) = @_;
  462. my $doubelmsgtime =
  463. 2; # zeit in sek in der doppelte nachrichten blockiert werden
  464. my $favcheck = $doubelmsgtime +
  465. 1; # zeit in der ein zweiter stop kommen muss/darf für fav
  466. my $testid = substr( $msg, 4, 8 );
  467. my $testcmd = substr( $msg, 12, 2 );
  468. my $timediff;
  469. my $name = $hash->{NAME};
  470. return "" if ( IsDisabled($name) );
  471. if ( my $lh = $modules{Siro}{defptr}{$testid} ) {
  472. my $name = $lh->{NAME};
  473. Log3 $hash, 5,
  474. "Siro_Parse: Incomming msg from IODevice $testid - $name device is defined";
  475. if ( defined($name)
  476. && $testcmd ne "54"
  477. ) # prüfe auf doppele msg falls gerät vorhanden und cmd nicht stop
  478. {
  479. Log3 $lh, 5,
  480. "Siro_Parse: Incomming msg $msg from IODevice name/DEF $testid - Hash -> $lh";
  481. my $testparsetime = gettimeofday();
  482. my $lastparse = $lh->{helper}{lastparse};
  483. my @lastparsearray = split( / /, $lastparse );
  484. if ( !defined( $lastparsearray[1] ) ) { $lastparsearray[1] = 0 }
  485. if ( !defined( $lastparsearray[0] ) ) { $lastparsearray[0] = "" }
  486. $timediff = $testparsetime - $lastparsearray[1];
  487. my $abort = "false";
  488. Log3 $lh, 5, "Siro_Parse: test doublemsg ";
  489. Log3 $lh, 5, "Siro_Parse: lastparsearray[0] -> $lastparsearray[0] ";
  490. Log3 $lh, 5, "Siro_Parse: lastparsearray[1] -> $lastparsearray[1] ";
  491. Log3 $lh, 5, "Siro_Parse: testparsetime -> $testparsetime ";
  492. Log3 $lh, 5, "Siro_Parse: timediff -> $timediff ";
  493. if ( $msg eq $lastparsearray[0] ) {
  494. if ( $timediff < $doubelmsgtime ) {
  495. $abort = "true";
  496. }
  497. }
  498. $lh->{helper}{lastparse} = "$msg $testparsetime";
  499. if ( $abort eq "true" ) {
  500. Log3 $lh, 4, "Siro_Parse: aborted , doublemsg ";
  501. return $name;
  502. }
  503. Log3 $lh, 4, "Siro_Parse: not aborted , no doublemsg ";
  504. }
  505. my ( undef, $rawData ) = split( "#", $msg );
  506. my $hlen = length($rawData);
  507. my $blen = $hlen * 4;
  508. my $bitData = unpack( "B$blen", pack( "H$hlen", $rawData ) );
  509. Log3 $hash, 5, "Siro_Parse: msg = $rawData length: $msg";
  510. Log3 $hash, 5, "Siro_Parse: rawData = $rawData length: $hlen";
  511. Log3 $hash, 5, "Siro_Parse: converted to bits: $bitData";
  512. my $id = substr( $rawData, 0, 7 ); # The first 7 hexcodes are the ID
  513. my $BitChannel = substr( $bitData, 28, 4 ); # Not needed atm
  514. my $channel = sprintf( "%d", hex( substr( $rawData, 7, 1 ) ) )
  515. ; # The last hexcode-char defines the channel
  516. my $channelhex = substr( $rawData, 7, 1 ); # tmp
  517. my $cmd = sprintf( "%d", hex( substr( $rawData, 8, 1 ) ) );
  518. my $newstate = $codes{ $cmd . $cmd }; # Set new state
  519. my $deviceCode = $id
  520. . $channelhex
  521. ; # Tmp change channel -> channelhex. The device-code is a combination of id and channel
  522. $debug = $debug . "id-" . $id . " ";
  523. Log3 $hash, 5, "Siro_Parse: device ID: $id";
  524. Log3 $hash, 5, "Siro_Parse: Channel: $channel";
  525. Log3 $hash, 5, "Siro_Parse: Cmd: $cmd Newstate: $newstate";
  526. Log3 $hash, 5, "Siro_Parse: deviceCode: $deviceCode";
  527. if ( defined($name)
  528. && $testcmd eq
  529. "54" ) # prüfe auf doppele msg falls gerät vorhanden und cmd stop
  530. {
  531. # Log3 $lh, 5, "Siro_Parse: prüfung auf douplestop ";
  532. my $testparsetime = gettimeofday();
  533. my $lastparsestop = $lh->{helper}{lastparse_stop};
  534. my $parseaborted = $lh->{helper}{parse_aborted};
  535. my @lastparsestoparray = split( / /, $lastparsestop );
  536. my $timediff = $testparsetime - $lastparsestoparray[1];
  537. my $abort = "false";
  538. $parseaborted = 0 if ( !defined($parseaborted) );
  539. Log3 $lh, 5, "Siro_Parse: test doublestop ";
  540. Log3 $lh, 5,
  541. "Siro_Parse: lastparsearray[0] -> $lastparsestoparray[0] ";
  542. Log3 $lh, 5,
  543. "Siro_Parse: lastparsearray[1] -> $lastparsestoparray[1] ";
  544. Log3 $lh, 5, "Siro_Parse: testparsetime -> $testparsetime ";
  545. Log3 $lh, 5, "Siro_Parse: timediff -> $timediff ";
  546. Log3 $lh, 5, "Siro_Parse: parseaborted -> $parseaborted ";
  547. if ( $newstate eq $lastparsestoparray[0] ) {
  548. if ( $timediff < 3 ) {
  549. $abort = "true";
  550. $parseaborted++;
  551. }
  552. }
  553. if ( $abort eq "true" && $parseaborted < 8 ) {
  554. $lh->{helper}{parse_aborted} = $parseaborted;
  555. Log3 $lh, 5, "Siro_Parse: aborted , doublestop ";
  556. return $name;
  557. }
  558. $lh->{helper}{lastparse_stop} = "$newstate $testparsetime";
  559. if ( $parseaborted >= 7 ) {
  560. $parseaborted = 0;
  561. $lh->{helper}{parse_aborted} = $parseaborted;
  562. $testparsetime = gettimeofday();
  563. $lh->{helper}{lastparse_stop} = "$newstate $testparsetime";
  564. if ( $newstate eq "stop" ) {
  565. Log3 $lh, 3,
  566. "Siro_Parse: double_msg signal_favoritenanfahrt erkannt ";
  567. $newstate = "fav";
  568. $args[0] = "fav";
  569. }
  570. }
  571. }
  572. if ( defined($name) ) {
  573. $args[0] = $newstate;
  574. my $parseaborted = 0;
  575. $lh->{helper}{parse_aborted} = $parseaborted;
  576. $debug = $debug . ' ' . $name;
  577. my $operationmode = AttrVal( $name, 'operation_mode', '0' );
  578. my $debugmode = AttrVal( $name, 'debug_mode', '0' );
  579. my $chan;
  580. if ( $operationmode eq '0' ) {
  581. $chan = AttrVal( $name, 'channel', undef );
  582. if ( !defined($chan) ) {
  583. $chan = $lh->{CHANNEL};
  584. }
  585. }
  586. if ( $operationmode eq '1' ) {
  587. $chan = AttrVal( $name, 'channel', undef );
  588. if ( !defined($chan) ) {
  589. $chan = $lh->{CHANNEL};
  590. }
  591. }
  592. my $aktMsg = $lh->{helper}{aktMsg};
  593. my @last_action_array = split( / /, $aktMsg );
  594. my $lastaction = $last_action_array[0];
  595. my $lastaction_position = $last_action_array[1];
  596. my $lastaction_time = $last_action_array[2];
  597. readingsSingleUpdate( $lh, "parsestate", $newstate, 1 );
  598. Log3 $lh, 5, "Siro_Parse: $name $newstate";
  599. Log3 $lh, 5, "Siro_Parse: operationmode -> $operationmode";
  600. if ( $operationmode ne '1' || $chan eq "0" ) {
  601. Log3 $lh, 5, "Siro_Parse: set mode to physical";
  602. $lh->{helper}{MODE} = "physical";
  603. $debug = $debug . ' physical';
  604. }
  605. else {
  606. $lh->{helper}{MODE} = "repeater";
  607. $debug = $debug . ' repeater';
  608. }
  609. if ( $chan eq "0" ) {
  610. $args[1] = "physical";
  611. $debug = $debug . ' physical';
  612. }
  613. Log3 $lh, 2, "Siro_Parse -> Siro_Set: $lh, $name, @args";
  614. Siro_Set( $lh, $name, @args );
  615. $debug = $debug . ' ' . $lh;
  616. if ( $debugmode eq "1" ) {
  617. readingsSingleUpdate( $lh, "DEBUG_PARSE", $debug, 1 );
  618. }
  619. ##############################################
  620. # Return list of affected devices
  621. #my ( $hash, $name, @args ) = @_;
  622. ##############################################
  623. return $name;
  624. }
  625. }
  626. else {
  627. my ( undef, $rawData ) = split( "#", $msg );
  628. my $hlen = length($rawData);
  629. my $blen = $hlen * 4;
  630. my $bitData = unpack( "B$blen", pack( "H$hlen", $rawData ) );
  631. Log3 $hash, 5, "Siro_Parse: msg = $rawData length: $msg";
  632. Log3 $hash, 5, "Siro_Parse: rawData = $rawData length: $hlen";
  633. Log3 $hash, 5, "Siro_Parse: converted to bits: $bitData";
  634. my $id = substr( $rawData, 0, 7 ); # The first 7 hexcodes are the ID
  635. my $BitChannel = substr( $bitData, 28, 4 ); # Not needed atm
  636. my $channel = sprintf( "%d", hex( substr( $rawData, 7, 1 ) ) )
  637. ; # The last hexcode-char defines the channel
  638. my $channelhex = substr( $rawData, 7, 1 ); # tmp
  639. my $cmd = sprintf( "%d", hex( substr( $rawData, 8, 1 ) ) );
  640. my $newstate = $codes{ $cmd . $cmd }; # Set new state
  641. my $deviceCode = $id
  642. . $channelhex
  643. ; # Tmp change channel -> channelhex. The device-code is a combination of id and channel
  644. $debug = $debug . "id-" . $id . " ";
  645. Log3 $hash, 5, "Siro_Parse: device ID: $id";
  646. Log3 $hash, 5, "Siro_Parse: Channel: $channel";
  647. Log3 $hash, 5, "Siro_Parse: Cmd: $cmd Newstate: $newstate";
  648. Log3 $hash, 5, "Siro_Parse: deviceCode: $deviceCode";
  649. Log3 $hash, 2, "Siro unknown device $deviceCode, please define it";
  650. return "UNDEFINED Siro_$deviceCode Siro $deviceCode";
  651. }
  652. }
  653. #############################################################
  654. sub Siro_Attr(@) {
  655. my ( $cmd, $name, $aName, $aVal ) = @_;
  656. my $hash = $defs{$name};
  657. return "\"Siro Attr: \" $name does not exist" if ( !defined($hash) );
  658. my $channel = ( $hash->{CHANNEL} );
  659. if ( $channel eq "0" ) {
  660. my @notallowed = (
  661. "prog_fav_sequence", "time_to_open",
  662. "time_to_close", "operation_mode",
  663. "channel_send_mode_1", "time_down_to_favorite"
  664. );
  665. foreach my $test (@notallowed) {
  666. if ( $test eq $aName ) {
  667. return
  668. "\"Siro Attr: \" $name is a group device, the attribute $aName $aVal is not allowed here.";
  669. }
  670. }
  671. }
  672. return undef if ( !defined($name) );
  673. return undef if ( !defined($aName) );
  674. #return undef if (!defined($aVal));
  675. if ( $cmd eq "set" ) {
  676. if ( $aName eq "debug_mode" && $aVal eq "0" ) {
  677. Log3 $hash, 5, "debug_mode: reading deleted";
  678. delete( $hash->{READINGS}{DEBUG_SEND} );
  679. delete( $hash->{READINGS}{DEBUG_PARSE} );
  680. delete( $hash->{READINGS}{DEBUG_SET} );
  681. }
  682. if ( $aName eq "debug_mode" && $aVal eq "1" ) {
  683. readingsSingleUpdate( $hash, "DEBUG_SEND", "aktiv", 1 );
  684. readingsSingleUpdate( $hash, "DEBUG_PARSE", "aktiv", 1 );
  685. readingsSingleUpdate( $hash, "DEBUG_SET", "aktiv", 1 );
  686. Log3 $hash, 5, "debug_mode: create reading";
  687. }
  688. if ( $aName eq "invers_position" ) {
  689. if ( $aVal eq "0" ) {
  690. my $oldicon =
  691. "{return '.*:fts_shutter_1w_'.(100-(int(\$state/10)*10))}";
  692. Log3 $hash, 3,
  693. "Siro_Attr: change attr old -> $attr{$name}{devStateIcon} $oldicon";
  694. if ( $attr{$name}{devStateIcon} eq $oldicon ) {
  695. Log3 $hash, 3, "Siro_Attr: ersetzt";
  696. $attr{$name}{devStateIcon} =
  697. "{return '.*:fts_shutter_1w_'.(int(\$state/10)*10)}";
  698. }
  699. if ( defined( $attr{$name}{down_auto_stop} ) ) {
  700. my $autostop = AttrVal( $name, 'down_auto_stop', 'undef' );
  701. $autostop = 100 - $autostop;
  702. $attr{$name}{down_auto_stop} = $autostop;
  703. }
  704. my $oldstate = $hash->{helper}{position};
  705. #Log3 $hash, 0, "Siro_Attr: ";
  706. if ( !defined $oldstate ) { $oldstate = 0 }
  707. my $newState = $oldstate;
  708. my $updateState;
  709. Log3 $hash, 3,
  710. "Siro_Attr: state old -> $oldstate new -> $newState";
  711. Siro_UpdateState( $hash, $newState, '', $updateState, 1 );
  712. }
  713. if ( $aVal eq "1" ) {
  714. my $oldicon =
  715. "{return '.*:fts_shutter_1w_'.(int(\$state/10)*10)}";
  716. Log3 $hash, 3,
  717. "Siro_Attr: change attr old -> $attr{$name}{devStateIcon} $oldicon";
  718. if ( $attr{$name}{devStateIcon} eq $oldicon ) {
  719. Log3 $hash, 3, "Siro_Attr: ersetzt";
  720. $attr{$name}{devStateIcon} =
  721. "{return '.*:fts_shutter_1w_'.(100-(int(\$state/10)*10))}";
  722. }
  723. if ( defined( $attr{$name}{down_auto_stop} ) ) {
  724. my $autostop = AttrVal( $name, 'down_auto_stop', 'undef' );
  725. $autostop = 100 - $autostop;
  726. $attr{$name}{down_auto_stop} = $autostop;
  727. }
  728. my $oldstate = $hash->{helper}{position};
  729. my $newState = 100 - $oldstate;
  730. my $updateState;
  731. Log3 $hash, 3,
  732. "Siro_Attr: state old -> $oldstate new -> $newState";
  733. Siro_UpdateState( $hash, $newState, '', $updateState, 1 );
  734. }
  735. }
  736. }
  737. if ( $cmd eq "del" ) {
  738. if ( $aName eq "invers_position" ) {
  739. my $oldicon =
  740. "{return '.*:fts_shutter_1w_'.(100-(int(\$state/10)*10))}";
  741. Log3 $hash, 3,
  742. "Siro_Attr delete invers: change attr old -> $attr{$name}{devStateIcon} $oldicon";
  743. if ( $attr{$name}{devStateIcon} eq $oldicon ) {
  744. Log3 $hash, 3, "Siro_Attr: ersetzt";
  745. $attr{$name}{devStateIcon} =
  746. "{return '.*:fts_shutter_1w_'.(int(\$state/10)*10)}";
  747. }
  748. my $oldstate = $hash->{helper}{position};
  749. my $newState = $oldstate;
  750. my $updateState;
  751. Log3 $hash, 3, "Siro_Attr: state old -> $oldstate new -> $newState";
  752. Siro_UpdateState( $hash, $newState, '', $updateState, 1 );
  753. }
  754. }
  755. return undef;
  756. }
  757. #################################################################
  758. # Call with hash, name, virtual/send, set-args
  759. sub Siro_Set($@) {
  760. my $testtimestart = gettimeofday();
  761. my $debug;
  762. my ( $hash, $name, @args ) = @_;
  763. return "" if ( IsDisabled($name) );
  764. # Set without argument #parseonly
  765. my $numberOfArgs = int(@args);
  766. my $nodrive = "false";
  767. $args[2] = "0";
  768. my $action = "no action";
  769. return "Siro_set: No set value specified" if ( $numberOfArgs < 1 );
  770. my $cmd = $args[0];
  771. my $invers = AttrVal( $name, 'invers_position', '0' );
  772. my $runningaction = ReadingsVal( $name, 'action', 'no action' );
  773. my $runningtime = 0;
  774. # teste auf hilfsvariablen
  775. my $save = ReadingsVal( $name, '.aktMsg', 'undef' );
  776. if ( $save eq 'undef' ) {
  777. $hash->{helper}{position} = "0";
  778. $hash->{helper}{aktMsg} = "stop" . ' ' . '0' . ' ' . gettimeofday();
  779. $hash->{helper}{lastMsg} = "stop" . ' ' . '0' . ' ' . gettimeofday();
  780. $hash->{helper}{lastProg} = "0";
  781. $hash->{helper}{lastparse_stop} = "stop" . ' ' . gettimeofday();
  782. $hash->{helper}{lastparse} = "";
  783. $hash->{helper}{parse_aborted} = "0";
  784. readingsSingleUpdate( $hash, ".aktMsg", $hash->{helper}{aktMsg}, 0 );
  785. readingsSingleUpdate( $hash, ".lastMsg", $hash->{helper}{lastMsg}, 0 );
  786. readingsSingleUpdate( $hash, ".lastProg", $hash->{helper}{lastProg},
  787. 0 );
  788. readingsSingleUpdate( $hash, ".lastparse", $hash->{helper}{lastparse},
  789. 0 );
  790. readingsSingleUpdate( $hash, ".lastparse_stop",
  791. $hash->{helper}{lastparse_stop}, 0 );
  792. readingsSingleUpdate( $hash, ".parse_aborted",
  793. $hash->{helper}{parse_aborted}, 0 );
  794. readingsSingleUpdate( $hash, ".positionsave",
  795. $hash->{helper}{position}, 0 );
  796. readingsSingleUpdate( $hash, ".positiontimer",
  797. $hash->{helper}{positiontimer}, 0 );
  798. }
  799. ######################################### für ECHO
  800. if ( $cmd =~ /^\d+$/ ) {
  801. if ( $cmd >= 0 && $cmd <= 100 ) {
  802. $args[0] = "position";
  803. $args[1] = $cmd;
  804. $cmd = "position";
  805. }
  806. }
  807. #########################################
  808. #diverse befehle bei laufender aktion abfangen
  809. if (
  810. (
  811. $cmd eq "position"
  812. || $cmd eq 'up_for_timer'
  813. || $cmd eq 'on_for_timer'
  814. )
  815. && $runningaction ne 'no action'
  816. )
  817. {
  818. my $restartmsg = $name . ' ' . $args[0] . ' ' . $args[1];
  819. Log3( $name, 3,
  820. "Siro_Set: laufende action gefunden - restart msg -> $restartmsg "
  821. );
  822. InternalTimer( gettimeofday() + 0, "Siro_Restartcmd", $restartmsg );
  823. $args[0] = "stop";
  824. $args[1] = '';
  825. $cmd = "stop";
  826. }
  827. #########################################
  828. # invertiere position
  829. if ( $cmd eq "position" && $invers eq "1" ) {
  830. my $noinvers = $args[1];
  831. my $inversposition = 100 - $args[1];
  832. $args[1] = $inversposition;
  833. Log3( $name, 3,
  834. "Siro_Set: invertiere Position - input -> $noinvers - inverted -> $inversposition "
  835. );
  836. }
  837. #########################################gettimeofday();
  838. if ( $cmd eq "reset_operating_seconds" ) {
  839. readingsSingleUpdate( $hash, "operating_seconds", '0', 1 ); #tmp
  840. readingsSingleUpdate( $hash, ".last_reset_operating_seconds",
  841. gettimeofday(), 0 );
  842. return;
  843. }
  844. # test anzahl der Tage
  845. if ( $hash->{CHANNEL} ne '0' ) {
  846. my $oldtime =
  847. ReadingsVal( $name, '.last_reset_operating_seconds', gettimeofday() );
  848. my $sec = gettimeofday() - $oldtime;
  849. my $min = int( $sec / 60 );
  850. $sec = $sec - ( $min * 60 );
  851. my $hour = int( $min / 60 );
  852. $min = $min - ( $hour * 60 );
  853. my $day = int( $hour / 24 );
  854. $hour = $hour - ( $day * 24 );
  855. # my $running = sprintf("%d %02d:%02d:%02d",$day,$hour,$min,$sec);
  856. if ( ReadingsVal( $name, 'last_reset_os', '' ) ne $day ) {
  857. readingsSingleUpdate( $hash, "last_reset_os", $day, 1 );
  858. }
  859. }
  860. else
  861. {
  862. readingsDelete( $hash, 'last_reset_os' );
  863. readingsDelete( $hash, 'operating_seconds' );
  864. }
  865. #########################################
  866. if ( !defined $args[2] ) {
  867. $args[2] = '';
  868. }
  869. if ( !defined $args[1] ) {
  870. $args[2] = '0';
  871. $args[1] = '';
  872. }
  873. if ( $args[1] eq "physical" ) {
  874. $hash->{helper}{MODE} = "physical";
  875. }
  876. if ( !defined($hash) ) { return; }
  877. if ( !defined($name) ) { return; }
  878. $debug = "$hash, $name, @args";
  879. if ( $cmd ne "?" ) {
  880. Log3( $name, 5, "Siro_Set: aufgerufen -> cmd -> $cmd args -> @args " );
  881. }
  882. #########################################
  883. # up/down for timer mappen auf on/off und timer für stop setzen
  884. if ( $cmd eq 'up_for_timer' ) {
  885. Log3( $name, 5, "Siro_Set: up_for_timer @args $args[1]" );
  886. $cmd = "off";
  887. InternalTimer( $testtimestart + $args[1], "Siro_Stop", $hash, 0 )
  888. ; # State auf Stopp
  889. $args[0] = $cmd;
  890. $action = 'up for timer ' . $args[1];
  891. readingsSingleUpdate( $hash, "action", $action, 1 ); #tmp
  892. }
  893. if ( $cmd eq 'down_for_timer' ) {
  894. Log3( $name, 3, "Siro_Set: down_for_timer @args $args[1]" );
  895. $cmd = "on";
  896. readingsSingleUpdate( $hash, "action", 'down for timer ' . $args[1],
  897. 1 ); #tmp
  898. InternalTimer( $testtimestart + $args[1], "Siro_Stop", $hash, 0 )
  899. ; # State auf Stopp
  900. $args[0] = $cmd;
  901. $action = 'down for timer ' . $args[1];
  902. readingsSingleUpdate( $hash, "action", $action, 1 ); #tmp
  903. }
  904. #########################################
  905. # diverse befehle auf bekannte befehle mappen
  906. if ( $cmd eq 'open' ) {
  907. $cmd = "off";
  908. $args[0] = $cmd;
  909. }
  910. if ( $cmd eq 'close' ) {
  911. $cmd = "on";
  912. $args[0] = $cmd;
  913. }
  914. #########################################
  915. $hash->{Version} = $version;
  916. # diverse attr einlesen
  917. my $debugmode = AttrVal( $name, 'debug_mode', '0' );
  918. my $testchannel = $hash->{CHANNEL};
  919. my $timetoopen =
  920. AttrVal( $name, 'time_to_open', 'undef' ); # fahrzeit komplett runter
  921. my $timetoclose =
  922. AttrVal( $name, 'time_to_close', 'undef' ); # fahrzeit komplett hoch
  923. my $operationmode =
  924. AttrVal( $name, 'operation_mode', '0' ); # mode 0 normal / 1 repeater
  925. my $limitedmode = "off";
  926. my $downlimit = AttrVal( $name, 'down_limit_mode_1', '100' )
  927. ; # maximale runterfahrt im mode 1
  928. my $autostop = AttrVal( $name, 'down_auto_stop', '100' )
  929. ; # automatischer stop bei runterfahrt
  930. #########################################
  931. # sets an device anpassen
  932. if ( $testchannel ne "0" ) {
  933. %sets = (
  934. "open" => "noArg",
  935. "close" => "noArg",
  936. "off" => "noArg",
  937. "stop" => "noArg",
  938. "on" => "noArg",
  939. "fav" => "noArg",
  940. "prog" => "noArg",
  941. "prog_stop" => "noArg",
  942. "position" => "slider,0,1,100"
  943. , # Wird nur bei vorhandenen time_to attributen gesetzt
  944. "state" => "noArg",
  945. "set_favorite" => "noArg",
  946. "down_for_timer" => "textField",
  947. "reset_operating_seconds" => "noArg",
  948. "up_for_timer" => "textField" # Ggf. entfernen (Alexa)
  949. );
  950. if ( $timetoopen eq 'undef' || $timetoclose eq 'undef' ) {
  951. if ( $attr{$name}{webCmd} eq "stop:on:off:fav:position" ) {
  952. $attr{$name}{webCmd} = "stop:on:off:fav";
  953. }
  954. $limitedmode = "on";
  955. $hash->{INFO} =
  956. "limited function without ATTR time_to_open / time_to_close / time_down_to_favorite";
  957. }
  958. else {
  959. if ( $attr{$name}{webCmd} eq "stop:on:off:fav" ) {
  960. $attr{$name}{webCmd} = "stop:on:off:fav:position";
  961. }
  962. delete( $hash->{INFO} );
  963. }
  964. }
  965. else # this block is for groupdevicec ( channel 0 )
  966. {
  967. Log3( $name, 5, "Siro_Set: Groupdevice erkannt " );
  968. %sets = (
  969. "open" => "noArg",
  970. "close" => "noArg",
  971. "off" => "noArg",
  972. "stop" => "noArg",
  973. "on" => "noArg",
  974. "fav" => "noArg",
  975. #"prog" => "noArg",
  976. #"prog_stop" => "noArg",
  977. "position" => "slider,0,1,100"
  978. , # Wird nur bei vorhandenen time_to attributen gesetzt
  979. "state" => "noArg",
  980. #"set_favorite" => "noArg",
  981. "down_for_timer" => "textField",
  982. "up_for_timer" => "textField"
  983. );
  984. if ( $cmd ne "?" ) #change
  985. {
  986. my $timetoopen = "15";
  987. my $timetoclose = "15";
  988. my $testhashtmp = "";
  989. my $testhashtmp1 = "";
  990. my $namex = "";
  991. my $testid = "";
  992. my $id = $hash->{ID};
  993. my @grouphash;
  994. my @groupnames;
  995. @groupnames = Siro_Testgroup( $hash, $id );
  996. $hash->{INFO} =
  997. "This is a group Device with limited functions affected the following devices:\n @groupnames";
  998. my $groupcommand =
  999. $cmd . "," . $args[0] . "," . $args[1] . "," . $hash;
  1000. $hash->{helper}{groupcommand} = $groupcommand;
  1001. InternalTimer( gettimeofday() + 0, "Siro_Setgroup", $hash, 1 );
  1002. }
  1003. $hash->{helper}{MODE} = "physical";
  1004. }
  1005. #########################################
  1006. # Check for unknown arguments
  1007. if ( !exists( $sets{$cmd} ) ) {
  1008. my @cList;
  1009. # Overwrite %sets with setList
  1010. my $atts = AttrVal( $name, 'setList', "" );
  1011. my %setlist = split( "[: ][ ]*", $atts );
  1012. foreach my $k ( sort keys %sets ) {
  1013. my $opts = undef;
  1014. $opts = $sets{$k};
  1015. $opts = $setlist{$k} if ( exists( $setlist{$k} ) );
  1016. if ( defined($opts) ) {
  1017. push( @cList, $k . ':' . $opts );
  1018. }
  1019. else {
  1020. push( @cList, $k );
  1021. }
  1022. } # end foreach
  1023. return "Unknown argument $cmd, choose one of " . join( " ", @cList );
  1024. }
  1025. #########################################
  1026. # undefinierte attr time to open/close abfangen
  1027. if ( ( $timetoopen eq 'undef' || $timetoclose eq 'undef' )
  1028. && $testchannel ne "0" )
  1029. {
  1030. Log3( $name, 1,
  1031. "Siro_Set:limited function without definition of time_to_close and time_to_open. Please define this attributes."
  1032. );
  1033. $nodrive = "true";
  1034. $timetoopen = 1;
  1035. $timetoclose = 1;
  1036. }
  1037. #########################################
  1038. # progmodus setzen
  1039. if ( $cmd eq "prog" ) {
  1040. $hash->{helper}{lastProg} =
  1041. gettimeofday() + 180; # 3 min programmiermodus
  1042. }
  1043. if ( $cmd eq "prog_stop" ) {
  1044. $hash->{helper}{lastProg} = gettimeofday();
  1045. return;
  1046. }
  1047. #########################################
  1048. # favoritenposition speichern
  1049. if ( $cmd eq "set_favorite" ) {
  1050. if ( $debugmode eq "1" ) {
  1051. readingsSingleUpdate( $hash, "DEBUG_SET", 'setze Favorite', 1 );
  1052. }
  1053. my $sequence;
  1054. my $sequenceraw = AttrVal( $name, 'prog_fav_sequence', '' );
  1055. if ( defined( $attr{$name}{time_down_to_favorite} ) ) {
  1056. $sequence = $sequenceraw . ',2,' . $sequenceraw;
  1057. Log3( $name, 0, "Siro_Set: delete and set favorit -> $sequence" );
  1058. }
  1059. else {
  1060. $sequence = $sequenceraw;
  1061. Log3( $name, 0, "Siro_Set: set favorit -> $sequence" );
  1062. }
  1063. $hash->{sequence} = $sequence; # 3 Min Programmiermodus
  1064. InternalTimer( gettimeofday() + 1, "Siro_Sequence", $hash, 0 )
  1065. ; # State auf stop setzen nach Erreichen der Fahrdauer
  1066. Log3( $name, 1, "Siro_Set:setting new favorite" );
  1067. return;
  1068. }
  1069. #########################################
  1070. # variablenberechnung für folgende funktionen
  1071. my $check = 'check';
  1072. my $bmode = $hash->{helper}{aktMsg};
  1073. if ( $bmode eq "repeater" ) {
  1074. $check = 'nocheck';
  1075. }
  1076. my $ondirekttime = $timetoclose / 100; # Zeit für 1 Prozent Runterfahrt
  1077. my $offdirekttime = $timetoopen / 100; # Zeit für 1 Prozent Hochfahrt
  1078. #my $runningtime;
  1079. my $timetorun;
  1080. my $newposstate;
  1081. if ( defined( $args[1] ) ) {
  1082. if ( $args[1] =~ /^\d+$/ ) {
  1083. $newposstate = $args[1]; # Anzufahrende Position in Prozent
  1084. Log3( $name, 5, "Siro_Set:newposstate -> $newposstate" );
  1085. $timetorun =
  1086. $timetoclose / 100 *
  1087. $newposstate
  1088. ; # Laufzeit von 0 prozent }bis zur anzufahrenden Position
  1089. }
  1090. }
  1091. my $virtual; # Kontrollvariable der Positionsanfahrt
  1092. my $newState;
  1093. my $updateState;
  1094. my $positiondrive;
  1095. my $state = $hash->{STATE};
  1096. my $aktMsg = $hash->{helper}{aktMsg};
  1097. my @last_action_array = split( / /, $aktMsg );
  1098. my $lastaction = $last_action_array[0];
  1099. my $lastaction_position = $last_action_array[1];
  1100. my $lastaction_time = $last_action_array[2];
  1101. my $befMsg = $hash->{helper}{lastMsg};
  1102. my @before_action_array = split( / /, $befMsg );
  1103. my $beforeaction_position = $before_action_array[1];
  1104. my $timebetweenmsg =
  1105. $testtimestart -
  1106. $last_action_array[2]; # Zeit zwischen dem aktuellen und letzten Befehl
  1107. $timebetweenmsg = ( int( $timebetweenmsg * 10 ) / 10 );
  1108. my $oldposition; # Alter Stand in Prozent
  1109. my $newposition
  1110. ; # Errechnende Positionsänderung in Prozent - > bei on plus alten Stand
  1111. my $finalposition; # Erreichter Rollostand in Prozent für state;
  1112. my $time_to_favorite = AttrVal( $name, 'time_down_to_favorite', 'undef' );
  1113. my $favorit_position;
  1114. my $mode = $hash->{helper}{MODE}; # Betriebsmodus virtual, physicalFP
  1115. my $lastprogmode = $hash->{helper}{lastProg};
  1116. my $testprogmode = $testtimestart;
  1117. my $testprog = int($testprogmode) - int($lastprogmode);
  1118. my $oldstate = ReadingsVal( $name, 'state', 100 );
  1119. Log3( $name, 5, "Siro_set: test auf double stop" );
  1120. Log3( $name, 5, "Siro_set: testprogmode -> $testprogmode" );
  1121. Log3( $name, 5, "Siro_set: lastprogmode -> $lastprogmode" );
  1122. Log3( $name, 5, "Siro_set: lastaction -> $lastaction" );
  1123. Log3( $name, 5, "Siro_set: cmd -> $cmd" );
  1124. # verhindern eines doppelten stoppbefehls ausser progmodus 1
  1125. if ( $testprogmode > $lastprogmode
  1126. ) # Doppelten Stoppbefehl verhindern, ausser progmodus aktiv
  1127. {
  1128. if ( $lastaction eq 'stop' && $cmd eq 'stop' && $check ne 'nocheck' ) {
  1129. Log3( $name, 5, "Siro_set: double stop, action aborted" );
  1130. readingsSingleUpdate( $hash, "prog_mode", "inaktiv ", 1 );
  1131. if ( $debugmode eq "1" ) {
  1132. $debug = "Siro_set: double stop, action aborted";
  1133. readingsSingleUpdate( $hash, "DEBUG_SET", $debug, 1 );
  1134. }
  1135. return;
  1136. }
  1137. }
  1138. else {
  1139. $testprog = $testprog * -1;
  1140. $virtual = "virtual";
  1141. readingsSingleUpdate( $hash, "prog_mode", "$testprog", 1 );
  1142. }
  1143. #########################################
  1144. # invertierung position
  1145. if ( $invers eq "1" ) {
  1146. $oldstate = 100 - $oldstate;
  1147. $autostop = 100 - $autostop;
  1148. }
  1149. #########################################
  1150. Log3( $name, 5,
  1151. "Siro_Set: teste autostop: $autostop < 100 $oldstate < $autostop - $cmd"
  1152. );
  1153. # erkennung autostopfunktiuon
  1154. if ( $cmd eq "on" && $autostop < 100 && $oldstate < $autostop ) {
  1155. $cmd = "position";
  1156. $args[1] = $autostop;
  1157. $newposstate = $args[1]; # position autostop
  1158. $timetorun = $timetoclose / 100 * $newposstate;
  1159. Log3( $name, 1,
  1160. "Siro_Set: autostop gefunden mapping auf position erfolgt: $newposstate "
  1161. );
  1162. }
  1163. #########################################
  1164. # erkennung downlimit funktion
  1165. if ( $downlimit < 100 && $operationmode eq "1" ) {
  1166. if ( $cmd eq 'position' && $downlimit < $newposstate ) {
  1167. $args[1] = $downlimit;
  1168. $newposstate = $args[1]; # Anzufahrende Position in Prozent
  1169. $timetorun = $timetoclose / 100 * $newposstate;
  1170. Log3( $name, 1,
  1171. "Siro_Set: drive down limit reached: $newposstate " );
  1172. }
  1173. if ( $cmd eq 'on' ) {
  1174. $cmd = "position";
  1175. $args[1] = $downlimit;
  1176. $newposstate = $args[1]; # Anzufahrende Position in Prozent
  1177. $timetorun = $timetoclose / 100 * $newposstate;
  1178. Log3( $name, 1,
  1179. "Siro_Set: drive down limit reached: $newposstate " );
  1180. }
  1181. }
  1182. #########################################
  1183. # on/off Umschaltung ohne zwischenzeitliches Anhalten
  1184. if ( $cmd eq 'on'
  1185. && $timebetweenmsg < $timetoopen
  1186. && $lastaction eq 'off' ) # Prüfe auf direkte Umschaltung on - off
  1187. {
  1188. $oldposition = $beforeaction_position;
  1189. $newposition = $timebetweenmsg / $offdirekttime;
  1190. $finalposition = $oldposition - $newposition;
  1191. if ( $limitedmode eq "on" ) {
  1192. $finalposition = "50";
  1193. }
  1194. $hash->{helper}{lastMsg} = $aktMsg;
  1195. $hash->{helper}{aktMsg} =
  1196. "stop" . ' ' . int($finalposition) . ' ' . gettimeofday();
  1197. $hash->{helper}{positiontimer} = $timebetweenmsg;
  1198. $hash->{helper}{position} = int($finalposition);
  1199. if ( $mode ne "physical" ) {
  1200. Siro_SendCommand( $hash, 'stop' );
  1201. }
  1202. $aktMsg = $hash->{helper}{aktMsg};
  1203. @last_action_array = split( / /, $aktMsg );
  1204. $lastaction = $last_action_array[0];
  1205. $lastaction_position = $last_action_array[1];
  1206. $lastaction_time = $last_action_array[2];
  1207. $befMsg = $hash->{helper}{lastMsg};
  1208. @before_action_array = split( / /, $befMsg );
  1209. $beforeaction_position = $before_action_array[1];
  1210. $timebetweenmsg =
  1211. $testtimestart -
  1212. $last_action_array[2]
  1213. ; # Zeit zwischen dem aktuellen und letzten Befehl
  1214. $timebetweenmsg = ( int( $timebetweenmsg * 10 ) / 10 );
  1215. }
  1216. #########################################
  1217. # off/on Umschaltung ohne zwischenzeitliches Anhalten
  1218. if ( $cmd eq 'off'
  1219. && $timebetweenmsg < $timetoclose
  1220. && $lastaction eq 'on' )
  1221. {
  1222. $oldposition = $beforeaction_position;
  1223. $newposition = $timebetweenmsg / $ondirekttime;
  1224. $finalposition = $oldposition + $newposition;
  1225. if ( $limitedmode eq "on" ) {
  1226. $finalposition = "50";
  1227. }
  1228. $hash->{helper}{lastMsg} = $aktMsg;
  1229. $hash->{helper}{aktMsg} =
  1230. "stop" . ' ' . int($finalposition) . ' ' . gettimeofday();
  1231. $hash->{helper}{positiontimer} = $timebetweenmsg;
  1232. if ( $mode ne "physical" ) {
  1233. Siro_SendCommand( $hash, 'stop' );
  1234. }
  1235. $aktMsg = $hash->{helper}{aktMsg};
  1236. @last_action_array = split( / /, $aktMsg );
  1237. $lastaction = $last_action_array[0];
  1238. $lastaction_position = $last_action_array[1];
  1239. $lastaction_time = $last_action_array[2];
  1240. $befMsg = $hash->{helper}{lastMsg};
  1241. @before_action_array = split( / /, $befMsg );
  1242. $beforeaction_position = $before_action_array[1];
  1243. $timebetweenmsg =
  1244. gettimeofday() -
  1245. $last_action_array[2]
  1246. ; # Zeit zwischen dem aktuellen und letzten Befehl
  1247. $timebetweenmsg = ( int( $timebetweenmsg * 10 ) / 10 );
  1248. }
  1249. #########################################
  1250. # Positionsberechnung bei einem Stopp-Befehl
  1251. if ( $cmd eq 'stop' ) {
  1252. # lösche interne timer - setze reading action auf no action
  1253. readingsSingleUpdate( $hash, "action", 'no action', 1 );
  1254. RemoveInternalTimer($hash);
  1255. Log3( $name, 5,
  1256. "Siro_Set: cmd stop timebetweenmsg -> $timebetweenmsg ondirekttime -> $ondirekttime offdirekttime -> $offdirekttime "
  1257. );
  1258. $oldposition = $beforeaction_position;
  1259. if ( $ondirekttime eq "0"
  1260. || $offdirekttime eq
  1261. "0" ) # Fehler division durch 0 abfanken bei ungesetzten attributen
  1262. {
  1263. Log3( $name, 5,
  1264. "Siro_Set: cmd stop -> Positionserrechnung ohne gesetzte Attribute , Finalposition wird auf 50 gesetzt "
  1265. );
  1266. $finalposition = "50";
  1267. $args[1] = $finalposition;
  1268. }
  1269. else {
  1270. Log3( $name, 5,
  1271. "Siro_Set: stop - Lastaction -> $lastaction $lastaction_position $oldposition"
  1272. );
  1273. if ( $lastaction eq 'position' ) {
  1274. if ( $lastaction_position > $oldposition ) {
  1275. $lastaction = "on";
  1276. }
  1277. else {
  1278. $lastaction = "off";
  1279. }
  1280. }
  1281. if ( $lastaction eq 'on' ) # Letzte Fahrt runter (on)
  1282. {
  1283. $newposition = $timebetweenmsg / $ondirekttime;
  1284. $finalposition = $oldposition + $newposition;
  1285. }
  1286. elsif ( $lastaction eq 'off' ) # Letzte Fahrt hoch (off)
  1287. {
  1288. $newposition = $timebetweenmsg / $offdirekttime;
  1289. $finalposition = $oldposition - $newposition;
  1290. }
  1291. elsif ( $lastaction eq 'fav' ) # Letzte Fahrt unbekannt
  1292. {
  1293. #Fahrtrichtung ermitteln - dafür Position von lastmsg nehmen $beforeaction_position
  1294. $favorit_position = $time_to_favorite / $ondirekttime;
  1295. Log3( $name, 5,
  1296. "Siro_Set: drive to position aborted (target position:$favorit_position %) : (begin possition $beforeaction_position %) "
  1297. );
  1298. if ( $favorit_position < $beforeaction_position ) # Fahrt hoch
  1299. {
  1300. $newposition = $timebetweenmsg / $offdirekttime;
  1301. $finalposition = $oldposition - $newposition;
  1302. }
  1303. if ( $favorit_position > $beforeaction_position ) # Fahrt runter
  1304. {
  1305. $newposition = $timebetweenmsg / $ondirekttime;
  1306. $finalposition = $oldposition + $newposition;
  1307. }
  1308. Log3( $name, 5, "Siro_Set position: $finalposition " );
  1309. }
  1310. if ( $finalposition < 0 ) { $finalposition = 0; }
  1311. if ( $finalposition > 100 ) { $finalposition = 100; }
  1312. if ( $limitedmode eq "on" ) { $finalposition = "50"; }
  1313. $finalposition = int($finalposition); # abrunden
  1314. $args[1] = $finalposition;
  1315. }
  1316. }
  1317. #########################################
  1318. # Hardware-Favorit anfahren
  1319. if ( $cmd eq 'fav' ) {
  1320. Log3( $name, 5, "Siro_Set fav: $cmd " );
  1321. if ( !defined $time_to_favorite ) {
  1322. $time_to_favorite = 5;
  1323. } # Tmp ggf. ändern
  1324. # if ( $time_to_favorite eq "undef")
  1325. # {
  1326. # $time_to_favorite=5;
  1327. # #return;
  1328. # }
  1329. if ( $ondirekttime eq "0"
  1330. || $offdirekttime eq
  1331. "0" ) # Fehler division durch 0 abfanken bei ungesetzten attributen
  1332. {
  1333. Log3( $name, 1,
  1334. "Siro_Set: set cmd fav -> Favoritberechnung ohne gesetzte Attribute , aktion nicht möglich"
  1335. );
  1336. return;
  1337. }
  1338. $favorit_position =
  1339. $time_to_favorite /
  1340. $ondirekttime
  1341. ; # Errechnet nur die Position, die von oben angefahren wurde. pos
  1342. $args[0] = $cmd;
  1343. $args[1] = int($favorit_position);
  1344. if ( $time_to_favorite eq 'undef' ) {
  1345. Log3( $name, 1,
  1346. "Siro_Set: function position limited without attr time_down_to_favorite"
  1347. );
  1348. $time_to_favorite = "1";
  1349. $args[1] = "50";
  1350. # new
  1351. if ( $mode ne "physical" ) {
  1352. $args[2] = 'longstop';
  1353. $args[0] = 'stop';
  1354. Siro_SendCommand( $hash, @args );
  1355. readingsSingleUpdate( $hash, "position", '50', 1 );
  1356. readingsSingleUpdate( $hash, "state", '50', 1 );
  1357. }
  1358. return;
  1359. }
  1360. $args[2] = 'longstop';
  1361. $hash->{helper}{lastMsg} = $aktMsg;
  1362. $hash->{helper}{aktMsg} =
  1363. $args[0] . ' ' . $args[1] . ' ' . gettimeofday();
  1364. $hash->{helper}{positiontimer} = $timebetweenmsg;
  1365. $cmd = 'stop';
  1366. $args[0] = $cmd;
  1367. if ( $mode ne "physical" ) {
  1368. Siro_SendCommand( $hash, @args );
  1369. }
  1370. my $position =
  1371. $hash->{helper}{position}; # Position für die Zeitberechnung speichern
  1372. $hash->{helper}{position} = int($favorit_position);
  1373. Siro_UpdateState( $hash, int($favorit_position), '', '', 1 );
  1374. #Fahrtrichtung ermitteln - dafür Position von lastmsg nehmen $beforeaction_position
  1375. $runningtime = 0;
  1376. if ( $favorit_position < $position ) # Fahrt hoch
  1377. {
  1378. readingsSingleUpdate( $hash, "action", 'up to favorite', 1 ); #tmp
  1379. my $change = $position - $favorit_position; # änderung in %
  1380. $runningtime = $change * $offdirekttime;
  1381. }
  1382. if ( $favorit_position > $position ) # Fahrt runter
  1383. {
  1384. readingsSingleUpdate( $hash, "action", 'down to favorite', 1 ); #tmp
  1385. my $change = $favorit_position - $position; # Änderung in %
  1386. $runningtime = $change * $ondirekttime;
  1387. }
  1388. InternalTimer( $testtimestart + $runningtime,
  1389. "Siro_Position_fav", $hash, 0 )
  1390. ; # state auf Stopp setzen nach Erreichen der Fahrtdauer
  1391. return;
  1392. }
  1393. #########################################
  1394. # Teste auf Position '0' oder '100' -> Mappen auf 'on' oder 'off'
  1395. if ( $cmd eq 'on' ) { $args[1] = "100"; }
  1396. if ( $cmd eq 'off' ) { $args[1] = "0"; }
  1397. #########################################
  1398. #Aktualisierung des Timers (Zeit zwischen den Befehlen, lastmsg und aktmsg)
  1399. $hash->{helper}{lastMsg} = $hash->{helper}{aktMsg};
  1400. $hash->{helper}{aktMsg} = $args[0] . ' ' . $args[1] . ' ' . gettimeofday();
  1401. $hash->{helper}{positiontimer} = $timebetweenmsg;
  1402. #########################################
  1403. if ( defined($newposstate) ) {
  1404. if ( $newposstate eq "0" ) {
  1405. $cmd = "off";
  1406. Log3( $name, 5, "recognized position 0 " );
  1407. }
  1408. elsif ( $newposstate eq "100" ) {
  1409. $cmd = "on";
  1410. Log3( $name, 5, "recognized position 100 " );
  1411. }
  1412. }
  1413. # Direkte Positionsanfahrt
  1414. if ( $cmd eq 'position' ) {
  1415. Log3( $name, 5, "Siro_Set: nodrive -> $nodrive" );
  1416. if (
  1417. (
  1418. $ondirekttime eq "0"
  1419. || $offdirekttime eq "0"
  1420. || $nodrive eq "true"
  1421. )
  1422. && $testchannel ne "0"
  1423. ) # Fehler division durch 0 abfanken bei ungesetzten attributen
  1424. {
  1425. Log3( $name, 1,
  1426. "Siro_Set: Positionsanfahrt ohne gesetzte Attribute , aktion nicht möglich -> abbruch"
  1427. );
  1428. return
  1429. "Positionsanfahrt ohne gesetzte Attribute time_to_open und time_to_close nicht moeglich";
  1430. }
  1431. my $aktposition = $hash->{helper}{position};
  1432. # Fahrt nach oben
  1433. if ( $newposstate < $aktposition ) {
  1434. if ( $action eq "no action" ) {
  1435. $action = 'up to position ' . $newposstate;
  1436. readingsSingleUpdate( $hash, "action", $action, 1 ); #tmp
  1437. }
  1438. $cmd = 'off';
  1439. my $percenttorun = $aktposition - $newposstate;
  1440. $runningtime = $percenttorun * $offdirekttime;
  1441. $timetorun = $runningtime;
  1442. Log3( $name, 5,
  1443. "Siro_Set: direkt positiondrive: -> timing:($runningtime = $percenttorun*$offdirekttime) -> open runningtime:$runningtime - modification in % :$percenttorun"
  1444. );
  1445. }
  1446. #Fahrt nach unten
  1447. if ( $newposstate > $aktposition ) {
  1448. if ( $action eq "no action" ) {
  1449. $action = 'down to position ' . $newposstate;
  1450. readingsSingleUpdate( $hash, "action", $action, 1 ); #tmp
  1451. }
  1452. $cmd = 'on';
  1453. my $percenttorun = $newposstate - $aktposition;
  1454. $runningtime = $percenttorun * $ondirekttime;
  1455. $timetorun = $runningtime;
  1456. Log3( $name, 5,
  1457. "Siro_Set: direkt positiondrive: -> timing: ($runningtime = $percenttorun*$ondirekttime) -> close runningtime:$runningtime - modification in % :$percenttorun"
  1458. );
  1459. }
  1460. $virtual = "virtual"; # keine Stateänderung
  1461. Log3( $name, 5,
  1462. "Siro_Set: direkt positiondrive: -> setting timer to $runningtime"
  1463. );
  1464. InternalTimer( $testtimestart + $runningtime,
  1465. "Siro_Position_down_stop", $hash, 0 );
  1466. }
  1467. #########################################
  1468. # abarbeitung weiterer befehle
  1469. if ( $cmd eq 'on' ) {
  1470. if ( $action eq "no action" ) {
  1471. $action = 'down ';
  1472. readingsSingleUpdate( $hash, "action", $action, 1 ); #tmp
  1473. # voraussichtliche fahrzeit berechnen bis 100%
  1474. my $aktposition = $hash->{helper}{position};
  1475. my $percenttorun = 100 - $aktposition;
  1476. $runningtime = $percenttorun * $ondirekttime;
  1477. $timetorun = $runningtime;
  1478. Log3( $name, 4,
  1479. "Siro_Set: aktposition -> $aktposition - percenttorun -> $percenttorun - ondirekttime -> $ondirekttime"
  1480. );
  1481. Log3( $name, 4,
  1482. "Siro_Set: voraussichtliche fahrdauer bis 100%: -> $runningtime"
  1483. );
  1484. InternalTimer( gettimeofday() + $timetorun + 1,
  1485. "Siro_Stopaction", $hash, 0 ); # action auf Stopp
  1486. }
  1487. $newState = '100';
  1488. $positiondrive = 100;
  1489. }
  1490. elsif ( $cmd eq 'off' ) {
  1491. if ( $action eq "no action" ) {
  1492. $action = 'up ';
  1493. readingsSingleUpdate( $hash, "action", $action, 1 ); #tmp
  1494. # voraussichtliche fahrzeit berechnen bis 0%
  1495. my $aktposition = $hash->{helper}{position};
  1496. my $percenttorun = $aktposition;
  1497. $runningtime = $percenttorun * $offdirekttime;
  1498. $timetorun = $runningtime;
  1499. Log3( $name, 4,
  1500. "Siro_Set: aktposition -> $aktposition - percenttorun -> $percenttorun - offdirekttime -> $offdirekttime"
  1501. );
  1502. Log3( $name, 4,
  1503. "Siro_Set: voraussichtliche fahrdauer bis 0%: -> $runningtime"
  1504. );
  1505. InternalTimer( gettimeofday() + $timetorun + 1,
  1506. "Siro_Stopaction", $hash, 0 ); # action auf Stopp
  1507. }
  1508. $newState = '0';
  1509. $positiondrive = 0;
  1510. }
  1511. elsif ( $cmd eq 'stop' ) {
  1512. $newState = $finalposition;
  1513. $positiondrive = $finalposition;
  1514. }
  1515. elsif ( $cmd eq 'fav' ) {
  1516. $newState = $favorit_position;
  1517. $positiondrive = $favorit_position;
  1518. }
  1519. else {
  1520. $newState = $state; #todo: Was mache ich mit der Positiondrive?
  1521. }
  1522. #########################################
  1523. if ( !defined($virtual) ) { $virtual = ""; }
  1524. if ( !defined($newposstate) ) { $newposstate = 0; }
  1525. if ( !defined($newposstate) ) { $newposstate = 0; }
  1526. if ( $virtual ne "virtual" ) # Kein Stateupdate beim Anfahren einer Position
  1527. {
  1528. if ( $newposstate < 0 ) { $newposstate = 0; }
  1529. if ( $newposstate > 100 ) { $newposstate = 100; }
  1530. $hash->{helper}{position} = $positiondrive;
  1531. Log3( $name, 5, "Siro_Set: stateupdate erfolgt -> $positiondrive" );
  1532. #########################################
  1533. # invertiere position
  1534. if ( $invers eq "1" ) {
  1535. my $noinvers = $newState;
  1536. my $inversposition = 100 - $newState;
  1537. $newState = $inversposition;
  1538. Log3( $name, 3,
  1539. "Siro_Set: invertiere Position vor setstate - berechnet -> $noinvers - inverted -> $newState "
  1540. );
  1541. }
  1542. #########################################
  1543. Siro_UpdateState( $hash, $newState, '', $updateState, 1 );
  1544. }
  1545. else {
  1546. Log3( $name, 5, "Siro_Set: kein stateupdate erfolgt" );
  1547. #Setze readings positiondrive und positiontime
  1548. if ( $newposstate < 0 ) { $newposstate = 0; }
  1549. if ( $newposstate > 100 ) { $newposstate = 100; }
  1550. $hash->{helper}{position} = $newposstate;
  1551. }
  1552. $args[0] = $cmd;
  1553. if ( !defined($mode) ) { $mode = "virtual" }
  1554. if ( $debugmode eq "1" ) {
  1555. readingsSingleUpdate( $hash, "DEBUG_SET", $debug, 1 );
  1556. }
  1557. if ( $mode ne "physical" ) {
  1558. Log3( $name, 5,
  1559. "Siro_set: handing over to Siro_Send_Command with following arguments: @args"
  1560. );
  1561. Siro_SendCommand( $hash, @args );
  1562. }
  1563. $hash->{helper}{MODE} = "virtual";
  1564. $hash->{helper}{LastMODE} = $mode;
  1565. my $testtimeend = gettimeofday();
  1566. $runningtime = $testtimeend - $testtimestart;
  1567. Log3( $name, 5, "Siro_set: runningtime -> $runningtime" );
  1568. return;
  1569. }
  1570. ###########################################################################
  1571. sub Siro_UpdateState($$$$$) {
  1572. my ( $hash, $newState, $move, $updateState, $doTrigger ) = @_;
  1573. readingsBeginUpdate($hash);
  1574. if ( $newState < 0 ) { $newState = 0; }
  1575. if ( $newState > 100 ) { $newState = 100; }
  1576. readingsBulkUpdate( $hash, "state", $newState );
  1577. readingsBulkUpdate( $hash, "position", $newState );
  1578. $hash->{state} = $newState;
  1579. #$hash->{helper}{position} = $newState;
  1580. readingsEndUpdate( $hash, $doTrigger );
  1581. }
  1582. #################################################################
  1583. sub Siro_Position_down_start($) {
  1584. my ($hash) = @_;
  1585. my $name = $hash->{NAME};
  1586. #my $timetoopen = AttrVal($name,'time_to_open', 'undef'); #tmp
  1587. #my $timetoclose = AttrVal($name,'time_to_close', 'undef');#tmp
  1588. my @args;
  1589. $args[0] = 'on';
  1590. my $virtual = 'virtual';
  1591. Siro_SendCommand( $hash, @args,, $virtual );
  1592. Log3 $name, 5, "Siro_Position_down_start: completed";
  1593. return;
  1594. }
  1595. #################################################################
  1596. sub Siro_Position_down_stop($) {
  1597. my ($hash) = @_;
  1598. my $name = $hash->{NAME};
  1599. my @args;
  1600. $args[0] = 'stop';
  1601. if ( !defined( $args[1] ) ) { $args[1] = ""; }
  1602. my $virtual = 'virtual';
  1603. Siro_SendCommand( $hash, @args, $virtual );
  1604. my $positiondrive = $hash->{helper}{position};
  1605. my $aktMsg = $hash->{helper}{aktMsg};
  1606. $hash->{helper}{lastMsg} = $aktMsg;
  1607. #$hash->{helper}{aktMsg} = $args[0].' '.$args[1].' '.gettimeofday();
  1608. $hash->{helper}{aktMsg} =
  1609. $args[0] . ' ' . $positiondrive . ' ' . gettimeofday();
  1610. # invertiere position
  1611. my $invers = AttrVal( $name, 'invers_position', '0' );
  1612. if ( $invers eq "1" ) {
  1613. my $noinvers = $positiondrive;
  1614. my $inversposition = 100 - $positiondrive;
  1615. $positiondrive = $inversposition;
  1616. Log3( $name, 3,
  1617. "Siro_Set: invertiere Position vor setstate - berechnet -> $noinvers - inverted -> $positiondrive "
  1618. );
  1619. }
  1620. readingsSingleUpdate( $hash, "action", 'no action', 1 ); #tmp
  1621. Siro_UpdateState( $hash, $positiondrive, '', '', 1 );
  1622. Log3 $name, 5,
  1623. "Siro_Position_down_stop: completed -> state:$positiondrive ";
  1624. return;
  1625. }
  1626. #################################################################
  1627. sub Siro_Position_fav($) {
  1628. my ($hash) = @_;
  1629. my $name = $hash->{NAME};
  1630. my @args;
  1631. my $aktMsg = $hash->{helper}{aktMsg};
  1632. $hash->{helper}{lastMsg} = $aktMsg;
  1633. my @last_action_array = split( / /, $aktMsg );
  1634. $hash->{helper}{aktMsg} =
  1635. 'stop' . ' ' . $last_action_array[1] . ' ' . gettimeofday();
  1636. readingsSingleUpdate( $hash, "action", 'no action', 1 ); #tmp
  1637. #my $addtime = gettimeofday() - $hash->{helper}{motorstart};
  1638. #my $oldmotortime = ReadingsVal( $name, 'operating_seconds', 0 );
  1639. #my $newtime = $oldmotortime + $addtime;
  1640. #my $rounded = int(100 * $newtime + 0.5) / 100;
  1641. #readingsSingleUpdate( $hash, "operating_seconds",$rounded , 1 );
  1642. Log3 $name, 5, "Siro_Position_fav: completed";
  1643. return;
  1644. }
  1645. #################################################################
  1646. sub Siro_Sequence($) {
  1647. my $debug;
  1648. my ($hash) = @_;
  1649. my $name = $hash->{NAME};
  1650. Log3( $name, 1, "Siro_Sequence:START" );
  1651. my @args;
  1652. my @sequence = split( /,/, $hash->{sequence} );
  1653. my $debugmode = AttrVal( $name, 'debug_mode', '0' );
  1654. my $cmd = shift @sequence;
  1655. my $timer = shift @sequence;
  1656. $debug = $debug . ' ' . $cmd . ' ' . $timer . ' ' . @sequence;
  1657. $hash->{sequence} = join( ",", @sequence ); # 3 min Programmiermodus
  1658. $args[0] = $cmd;
  1659. Siro_SendCommand( $hash, @args, 'virtual' );
  1660. if ( defined($timer) ) {
  1661. InternalTimer( gettimeofday() + $timer, "Siro_Sequence", $hash, 0 );
  1662. $debug = $debug . '- Erneute Aufrufsequenz';
  1663. }
  1664. else {
  1665. $debug = $debug
  1666. . '- Sequenz beendet, ATTR time_to _fav neu berechnet und gesetzt, progmode beendet';
  1667. readingsSingleUpdate( $hash, "prog_mode", "inaktiv ", 1 );
  1668. delete( $hash->{sequence} );
  1669. my $timetoclose = AttrVal( $name, 'time_to_close', '10' );
  1670. my $ondirekttime = $timetoclose / 100; # Zeit für 1 Prozent Runterfahrt
  1671. my $position = $hash->{helper}{position};
  1672. my $newfav = $ondirekttime * $position;
  1673. $attr{$name}{time_down_to_favorite} = $newfav;
  1674. }
  1675. if ( $debugmode eq "1" ) {
  1676. readingsSingleUpdate( $hash, "DEBUG_SEQUENCE", $debug, 1 );
  1677. }
  1678. Log3 $name, 5, "Siro_Sequence: completed";
  1679. return;
  1680. }
  1681. ###############################################################
  1682. sub Siro_Setgroup($) {
  1683. my ($hash) = @_;
  1684. my $name = $hash->{NAME};
  1685. my $grouphashraw = $hash->{helper}{affected_devices_h};
  1686. my @grouphash = split( /,/, $grouphashraw );
  1687. my $groupnamesraw = $hash->{helper}{affected_devices_n};
  1688. my @groupnames = split( /,/, $groupnamesraw );
  1689. my $groupcommandraw = $hash->{helper}{groupcommand};
  1690. my @groupcommand = split( /,/, $groupcommandraw );
  1691. Log3( $name, 5, "Siro_Setgroup : @groupnames -> $groupcommandraw " );
  1692. my $count = 0;
  1693. foreach my $senddevice (@groupnames) {
  1694. my @args;
  1695. #Log3($name,5,"----------------------------");
  1696. Log3( $name, 5, "Siro_Setgroup: count -> $count " );
  1697. Log3( $name, 5, "Siro_Setgroup: senddevice -> $senddevice " );
  1698. Log3( $name, 5, "Siro_Setgroup: testhash -> $grouphash[$count] " );
  1699. Log3( $name, 5,
  1700. "Siro_Setgroup: command -> $groupcommand[1] $groupcommand[2] " );
  1701. #Log3($name,5,"----------------------------");
  1702. $args[0] = $groupcommand[1];
  1703. $args[1] = $groupcommand[2];
  1704. Log3( $name, 5,
  1705. "Siro_Setgroup: aufruf -> $grouphash[$count],$senddevice, @args "
  1706. );
  1707. Log3( $name, 5,
  1708. "Siro_Setgroup: set $senddevice $groupcommand[1] $groupcommand[2]"
  1709. );
  1710. #my $cs = "set Siro_5B417081 on";
  1711. my $cs = "set $senddevice $groupcommand[0] $groupcommand[2]";
  1712. my $client_hash = $grouphash[$count];
  1713. Log3( $name, 5, "Siro_Setgroup: command -> " . $cs );
  1714. my $errors = AnalyzeCommandChain( undef, $cs );
  1715. $count++;
  1716. }
  1717. return;
  1718. }
  1719. ###############################################################
  1720. sub Siro_Stop($) {
  1721. my ($hash) = @_;
  1722. my $name = $hash->{NAME};
  1723. my @args;
  1724. $args[0] = "stop";
  1725. #my ( $hash, $name, @args ) = @_;
  1726. readingsSingleUpdate( $hash, "action", 'no action', 1 ); #tmp
  1727. Log3( $name, 0, "Siro_Stop: x-for-timer stop -> @args " );
  1728. Siro_Set( $hash, $name, @args );
  1729. return;
  1730. }
  1731. ###############################################################
  1732. sub Siro_Restartcmd($) {
  1733. my $incomming = $_[0];
  1734. my @msgarray = split( / /, $incomming );
  1735. my $name = $msgarray[1];
  1736. #my $hash = $msgarray[1];
  1737. #my $name = $hash->{NAME};
  1738. my $cs = "set $incomming";
  1739. Log3( $name, 3, "Siro_Restartcmd: incomming -> $incomming " );
  1740. Log3( $name, 3, "Siro_Restartcmd: command -> " . $cs );
  1741. my $errors = AnalyzeCommandChain( undef, $cs );
  1742. return;
  1743. }
  1744. ###############################################################
  1745. sub Siro_Stopaction($) {
  1746. my ($hash) = @_;
  1747. my $name = $hash->{NAME};
  1748. Log3( $name, 5, "Siro_Stopaction: setze no action " );
  1749. readingsSingleUpdate( $hash, "action", 'no action', 1 ); #tmp
  1750. if ( $hash->{CHANNEL} ne '0' ) {
  1751. my $addtime = gettimeofday() - $hash->{helper}{motorstart};
  1752. my $oldmotortime = ReadingsVal( $name, 'operating_seconds', 0 );
  1753. my $newtime = $oldmotortime + $addtime;
  1754. my $rounded = int( 100 * $newtime + 0.5 ) / 100;
  1755. readingsSingleUpdate( $hash, "operating_seconds", $rounded, 1 );
  1756. }
  1757. return;
  1758. }
  1759. ###############################################################readingsSingleUpdate($hash, "action",$action, 1); #tmp
  1760. sub Siro_Testgroup($$) {
  1761. my ( $hash, $id ) = @_;
  1762. my $name = $hash->{NAME};
  1763. my $testid;
  1764. my $testidchan;
  1765. my @groupnames;
  1766. my @grouphash;
  1767. foreach my $testdevices ( keys %{ $modules{Siro}{defptr} } ) #
  1768. {
  1769. $testid = substr( $testdevices, 0, 7 );
  1770. $testidchan = substr( $testdevices, 7, 1 );
  1771. Log3( $name, 5,
  1772. "Siro_Testgroup: groupdevice search device $testid -> test device -> $testdevices-$testidchan "
  1773. );
  1774. if ( $id eq $testid ) {
  1775. my $lh = $modules{Siro}{defptr}{$testdevices}; #def
  1776. my $namex = $lh->{NAME};
  1777. my $channelx = $lh->{CHANNEL};
  1778. Log3( $name, 5,
  1779. "Siro_Testgroup: device for group found -> $namex lh -$lh" );
  1780. if ( $channelx ne "0" ) {
  1781. Log3( $name, 5,
  1782. "Siro_Testgroup: device for group found -> $namex hash -> $lh"
  1783. );
  1784. push( @groupnames, $namex );
  1785. push( @grouphash, $lh ); # betreffendes hash zur gruppe zufügen
  1786. }
  1787. }
  1788. }
  1789. my $hashstring;
  1790. foreach my $target (@grouphash) {
  1791. $hashstring = $hashstring . $target . ",";
  1792. }
  1793. chop($hashstring);
  1794. $hash->{helper}{affected_devices_h} = "$hashstring";
  1795. my $devicestring;
  1796. foreach my $target (@groupnames) {
  1797. $devicestring = $devicestring . $target . ",";
  1798. }
  1799. chop($devicestring);
  1800. $hash->{helper}{affected_devices_n} = $devicestring;
  1801. return @groupnames;
  1802. }
  1803. #############################################
  1804. sub Siro_icon($) {
  1805. #return 'fts_shutter_1w_'.(int($state/10)*10);
  1806. my ($id) = @_;
  1807. my $lh = $modules{Siro}{defptr}{$id};
  1808. my $position = $lh->{helper}{position};
  1809. my $xsvg = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>
  1810. <!-- Created with Inkscape (http://www.inkscape.org/) -->
  1811. <svg
  1812. xmlns:dc=\"http://purl.org/dc/elements/1.1/\"
  1813. xmlns:cc=\"http://creativecommons.org/ns#\"
  1814. xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"
  1815. xmlns:svg=\"http://www.w3.org/2000/svg\"
  1816. xmlns=\"http://www.w3.org/2000/svg\"
  1817. version=\"1.0\"
  1818. width=\"585\"
  1819. height=\"585\"
  1820. viewBox=\"0 0 585 585\"
  1821. id=\"svg2\">
  1822. <defs
  1823. id=\"defs12\" />
  1824. <metadata
  1825. id=\"metadata4\">
  1826. Created by potrace 1.8, written by Peter Selinger 2001-2007
  1827. <rdf:RDF>
  1828. <cc:Work
  1829. rdf:about=\"\">
  1830. <dc:format>image/svg+xml</dc:format>
  1831. <dc:type
  1832. rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" />
  1833. <dc:title></dc:title>
  1834. </cc:Work>
  1835. </rdf:RDF>
  1836. </metadata>
  1837. <path
  1838. d=\"m 14.173228,80.136391 442.204712,0 0,29.186439 -442.204712,0 z\"
  1839. id=\"rect3764\"
  1840. style=\"fill:#000000;fill-opacity:1;stroke-width:3.1960001;stroke-linecap:round;stroke-miterlimit:4\" />
  1841. </svg>
  1842. ";
  1843. return $xsvg;
  1844. }
  1845. 1;
  1846. =pod
  1847. =item summary Supports rf shutters from Siro
  1848. =item summary_DE Unterst&uumltzt Siro Rollo-Funkmotoren
  1849. =begin html
  1850. <a name="Siro"></a>
  1851. <h3>Siro protocol</h3>
  1852. <ul>
  1853. <br> A <a href="#SIGNALduino">SIGNALduino</a> device (must be defined first).<br>
  1854. <br>
  1855. Since the protocols of Siro and Dooya are very similar, it is currently difficult to operate these systems simultaneously via one "IODev". Sending commands works without any problems, but distinguishing between the remote control signals is hardly possible in SIGNALduino. For the operation of the Siro-Module it is therefore recommended to exclude the Dooya protocol (16) in the SIGNALduino, via the whitelist. In order to detect the remote control signals correctly, it is also necessary to deactivate the "manchesterMC" protocol (disableMessagetype manchesterMC) in the SIGNALduino. If machester-coded commands are required, it is recommended to use a second SIGNALduino.<br>
  1856. <br>
  1857. <br>
  1858. <a name="Sirodefine"></a>
  1859. <br>
  1860. <b>Define</b>
  1861. <br>
  1862. <ul>
  1863. <code>define&lt; name&gt; Siro &lt;id&gt;&lt;channel&gt; </code>
  1864. <br>
  1865. <br>
  1866. The ID is a 7-digit hex code, which is uniquely and firmly assigned to a Siro remote control. Channel is the single-digit channel assignment of the remote control and is also hexadecimal. This results in the possible channels 0 - 15 (hexadecimal 0-F).
  1867. A unique ID must be specified, the channel (channel) must also be specified.
  1868. An autocreate (if enabled) automatically creates the device with the ID of the remote control and the channel.
  1869. <br><br>
  1870. Examples:<br><br>
  1871. <ul>
  1872. <code>define Siro1 Siro AB00FC1</code><br> Creates a Siro-device called Siro1 with the ID: AB00FC and Channel: 1<br>
  1873. </ul>
  1874. </ul>
  1875. <br>
  1876. <a name="Siroset"></a>
  1877. <b>Set </b><br>
  1878. <ul>
  1879. <code>set &lt;name&gt; &lt;value&gt; [&lt;position&gt]</code>
  1880. <br><br>
  1881. where <code>value</code> is one of:<br>
  1882. <pre>
  1883. on
  1884. off
  1885. stop
  1886. pos (0...100)
  1887. prog
  1888. fav
  1889. </pre>
  1890. Examples:<br><br>
  1891. <ul>
  1892. <code>set Siro1 on</code><br>
  1893. <code>set Siro1 off</code><br>
  1894. <code>set Siro1 position 50</code><br>
  1895. <code>set Siro1 fav</code><br>
  1896. <code>set Siro1 stop</code><br>
  1897. <code>set Siro1 set_favorite</code><br>
  1898. </ul>
  1899. <br>
  1900. <ul>
  1901. set Siro1 on moves the roller blind up completely (0%)<br>
  1902. set Siro1 off moves the roller blind down completely (100%)<br>
  1903. set Siro1 stop stops the current movement of the roller blind<br>
  1904. set Siro1 position 45 moves the roller blind to the specified position (45%)<br>
  1905. set Siro1 45 moves the roller blind to the specified position (45%)<br>
  1906. set Siro1 fav moves the blind to the hardware-programmed favourite middle position<br>
  1907. set Siro1 prog corresponds to the "P2" button on the remote control, the module is set to programming mode (3 min).<br>
  1908. set Siro1 set_favorite programs the current roll position as hardware middle position. The attribute time_down_to_favorite is recalculated and set. <br>
  1909. </ul>
  1910. <br>
  1911. Notes:<br><br>
  1912. <ul>
  1913. <li>If the module is in programming mode, the module detects successive stop commands because they are absolutely necessary for programming. In this mode, the readings and state are not updated. The mode is automatically terminated after 3 minutes. The remaining time in programming mode is displayed in the reading "pro_mode". The remaining time in programming mode is displayed in the reading "pro_mode". The programming of the roller blind must be completed during this time, otherwise the module will no longer accept successive stop commands. The display of the position, the state, is a calculated position only, since there is no return channel to status message. Due to a possible missing remote control command, timing problem etc. it may happen that this display shows wrong values sometimes. When moving into an end position without stopping the movement (set Siro1[on/off]), the status display and real position are synchronized each time the position is reached. This is due to the hardware and unfortunately not technically possible.
  1914. </li>
  1915. </ul>
  1916. </ul>
  1917. <br>
  1918. <b>Get</b>
  1919. <ul>N/A</ul><br>
  1920. <a name="Siroattr"></a>
  1921. <b>Attributes</b><br><br>
  1922. <ul>
  1923. <a name="IODev"></a>
  1924. <li>IODev<br>
  1925. The IODev must contain the physical device for sending and receiving the signals. Currently a SIGNALduino or SIGNALesp is supported.
  1926. Without the specification of the "Transmit and receive module" "IODev", a function is not possible.
  1927. </li><br>
  1928. <a name="channel"></a>
  1929. <li>channel (since V1.09 no longer available)<br>
  1930. contains the channel used by the module for sending and receiving.
  1931. This is already set when the device is created.
  1932. </li><br>
  1933. <a name="channel_send_mode_1 "></a>
  1934. <li>channel_send_mode_1 <br>
  1935. contains the channel that is used by the module in "operation_mode 1" to send.
  1936. This attribute is not used in "operation_mode 0"
  1937. </li><br>
  1938. <a name="operation_mode"></a>
  1939. <li>operation_mode<br>
  1940. Mode 0<br><br>
  1941. This is the default mode. In this mode, the module uses only the channel specified by the remote control or the "channel" attribute. In the worst case, signals, timing problems etc. missed by FHEM can lead to wrong states and position readings. These are synchronized again when a final position is approached.
  1942. <br><br>Mode 1<br><br>
  1943. Extended mode. In this mode, the module uses two channels. The standard channel "channel" for receiving the remote control. This should no longer be received by the blind itself. And the "channel_send_mode_1", for sending to the roller blind motor. For this purpose, a reconfiguration of the motor is necessary. This mode is "much safer" in terms of the representation of the states, since missing a signal by FHEM does not cause the wrong positions to be displayed. The roller blind only moves when FHEM has received the signal and passes it on to the motor.<br>
  1944. Instructions for configuring the motor will follow.
  1945. </li><br>
  1946. <a name="time_down_to_favorite"></a>
  1947. <li>time_down_to_favorite<br>
  1948. contains the movement time in seconds, which the roller blind needs from 0% position to the hardware favorite center position. This time must be measured and entered manually.
  1949. Without this attribute, the module is not fully functional.</li><br>
  1950. <a name="time_to_close"></a>
  1951. <li>time_to_close<br>
  1952. contains the movement time in seconds required by the blind from 0% position to 100% position. This time must be measured and entered manually.
  1953. Without this attribute, the module is not fully functional.</li><br>
  1954. <a name="time_to_open"></a>
  1955. <li>time_to_open<br>
  1956. contains the movement time in seconds required by the blind from 100% position to 0% position. This time must be measured and entered manually.
  1957. Without this attribute, the module is not fully functional.</li><br>
  1958. <a name="prog_fav_sequence"></a>
  1959. <li>prog_fav_sequence<br>
  1960. contains the command sequence for programming the hardware favorite position</li><br>
  1961. <a name="debug_mode [0:1]"></a>
  1962. <li>debug_mode [0:1]<br>
  1963. In mode 1, additional readings are created for troubleshooting purposes, in which the output of all module elements is output. Commands are NOT physically sent.</li><br>
  1964. <a name="Info"></a>
  1965. <li>Info<br>
  1966. The attributes webcmd and devStateIcon are set once when the device is created and are adapted to the respective mode of the device during operation. The adaptation of these contents only takes place until they have been changed by the user. After that, there is no longer any automatic adjustment.</li><br>
  1967. </ul>
  1968. </ul>
  1969. =end html
  1970. =begin html_DE
  1971. <a name="Siro"></a>
  1972. <h3>Siro protocol</h3>
  1973. <ul>
  1974. <br> Ein <a href="#SIGNALduino">SIGNALduino</a>-Geraet (dieses sollte als erstes angelegt sein).<br>
  1975. <br>
  1976. Da sich die Protokolle von Siro und Dooya sehr &auml;hneln, ist ein gleichzeitiger Betrieb dieser Systeme ueber ein "IODev" derzeit schwierig. Das Senden von Befehlen funktioniert ohne Probleme, aber das Unterscheiden der Fernbedienungssignale ist in Signalduino kaum m&ouml;glich. Zum Betrieb der Siromoduls wird daher empfohlen, das Dooyaprotokoll im SIGNALduino (16) &uuml;ber die Whitelist auszuschliessen. Zur fehlerfreien Erkennung der Fernbedienungssignale ist es weiterhin erforderlich im SIGMALduino das Protokoll "manchesterMC" zu deaktivieren (disableMessagetype manchesterMC). Wird der Empfang von machestercodierten Befehlen benoetigt, wird der Betrieb eines zweiten Signalduinos empfohlen.<br>
  1977. <br>
  1978. <br>
  1979. <a name="Sirodefine"></a>
  1980. <br>
  1981. <b>Define</b>
  1982. <br>
  1983. <ul>
  1984. <code>define &lt;name&gt; Siro &lt;id&gt; &lt;channel&gt;</code>
  1985. <br>
  1986. <br>
  1987. Bei der <code>&lt;ID&gt;</code> handelt es sich um einen 7-stelligen Hexcode, der einer Siro Fernbedienung eindeutig und fest zugewiesen ist. <code>&lt;Channel&gt;</code> ist die einstellige Kanalzuweisung der Fernbedienung und ist ebenfalls hexadezimal. Somit ergeben sich die m&ouml;glichen Kan&auml;le 0 - 15 (hexadezimal 0-F).
  1988. Eine eindeutige ID muss angegeben werden, der Kanal (Channel) muss ebenfalls angegeben werden. <br>
  1989. Ein Autocreate (falls aktiviert), legt das Device mit der ID der Fernbedienung und dem Kanal automatisch an.
  1990. <br><br>
  1991. Beispiele:<br><br>
  1992. <ul>
  1993. <code>define Siro1 Siro AB00FC1</code><br> erstellt ein Siro-Geraet Siro1 mit der ID: AB00FC und dem Kanal: 1<br>
  1994. </ul>
  1995. </ul>
  1996. <br>
  1997. <a name="Siroset"></a>
  1998. <b>Set </b><br>
  1999. <ul>
  2000. <code>set &lt;name&gt; &lt;value&gt; [&lt;position&gt]</code>
  2001. <br><br>
  2002. where <code>value</code> is one of:<br>
  2003. <pre>
  2004. on
  2005. off
  2006. stop
  2007. pos (0..100)
  2008. prog
  2009. fav
  2010. </pre>
  2011. Beispiele:<br><br>
  2012. <ul>
  2013. <code>set Siro1 on</code><br>
  2014. <code>set Siro1 off</code><br>
  2015. <code>set Siro1 position 50</code><br>
  2016. <code>set Siro1 fav</code><br>
  2017. <code>set Siro1 stop</code><br>
  2018. <code>set Siro1 set_favorite</code><br>
  2019. <code>set Siro1 down_for_timer 5</code><br>
  2020. <code>set Siro1 up_for_timer 5</code><br>
  2021. <code>set Siro1 set_favorite</code><br>
  2022. </ul>
  2023. <br>
  2024. <ul>
  2025. set Siro1 on f&auml;hrt das Rollo komplett hoch (0%)<br>
  2026. set Siro1 off f&auml;hrt das Rollo komplett herunter (100%)<br>
  2027. set Siro1 stop stoppt die aktuelle Fahrt des Rollos<br>
  2028. set Siro1 position 45 f&auml;hrt das Rollo zur angegebenen Position (45%)<br>
  2029. set Siro1 45 f&auml;hrt das Rollo zur angegebenen Position (45%)<br>
  2030. set Siro1 fav f&auml;hrt das Rollo in die hardwarem&auml;ssig programmierte Mittelposition<br>
  2031. et Siro1 down_for_timer 5 f&auml;hrt das Rollo 5 Sekunden nach unten<br>
  2032. et Siro1 uown_for_timer 5 f&auml;hrt das Rollo 5 Sekunden nach oben<br>
  2033. set Siro1 prog entspricht der "P2" Taste der Fernbedienung. Das Modul wird in den Programmiermodus versetzt (3 Min.)<br>
  2034. set Siro1 set_favorite programmiert den aktuellen Rollostand als Hardwaremittelposition, das ATTR time_down_to_favorite wird neu berechnet und gesetzt. <br>
  2035. </ul>
  2036. <br>
  2037. Hinweise:<br><br>
  2038. <ul>
  2039. <li>Befindet sich das Modul im Programmiermodus, werden aufeinanderfolgende Stoppbefehle vom Modul erkannt, da diese zur Programmierung zwingend erforderlich sind. In diesem Modus werden die Readings und das State nicht aktualisiert. Der Modus wird nach 3 Minuten automatisch beendet. Die verbleibende Zeit im Programmiermodus wird im Reading "pro_mode" dargestellt. Die Programmierung des Rollos muss in dieser Zeit abgeschlossen sein, da das Modul andernfalls keine aufeinanderfolgenden Stoppbefehle mehr akzeptiert.
  2040. Die Anzeige der Position, des States, ist eine ausschliesslich rechnerisch ermittelte Position, da es keinen R&uumlckkanal zu Statusmeldung gibt. Aufgrund eines ggf. verpassten Fernbedienungsbefehls, Timingproblems etc. kann es vorkommen, dass diese Anzeige ggf. mal falsche Werte anzeigt. Bei einer Fahrt in eine Endposition, ohne die Fahrt zu stoppen (set Siro1 [on/off]), werden Statusanzeige und echte Position bei Erreichen der Position jedes Mal synchronisiert. Diese ist der Hardware geschuldet und technisch leider nicht anders l&ouml;sbar.
  2041. </li>
  2042. </ul>
  2043. </ul>
  2044. <br>
  2045. <b>Get</b>
  2046. <ul>N/A</ul><br>
  2047. <a name="Siroattr"></a>
  2048. <b>Attributes</b><br><br>
  2049. <ul>
  2050. <a name="IODev"></a>
  2051. <li>IODev<br>
  2052. Das IODev muss das physische Ger&auml;t zum Senden und Empfangen der Signale enthalten. Derzeit wird ein SIGNALduino bzw. SIGNALesp unterstützt.
  2053. Ohne der Angabe des "Sende- und Empfangsmodul" "IODev" ist keine Funktion moeglich.</li><br>
  2054. <a name="channel_send_mode_1 "></a>
  2055. <li>channel_send_mode_1 <br>
  2056. Beinhaltet den Kanal, der vom Modul im "operation_mode 1" zum Senden genutzt wird.
  2057. Dieses Attribut wird in "operation_mode 0" nicht genutzt
  2058. </li><br>
  2059. <a name="down_limit_mode_1 "></a>
  2060. <li>down_limit_mode_1 <br>
  2061. Bei gesetztem Attribut wird das Rollo bei einem Fahrt nach unten nur bis zur angegebenen Position gefahren, egal ob das Kommando über das Modul oder die Fernbedienung ausgloest wurde. Diese Funktion ist nur im Mode 1 aktiv.
  2062. </li><br>
  2063. <a name="down_auto_stop "></a>
  2064. <li>down_auto_stop <br>
  2065. Bei gesetztem Attribut fährt das Rollo bei einer Fahrt nach unten nur bis zur angegebenen Position. Eine weiterfahrt erfolgt nach nochmaligem Befehl. Diese Funktion greift nur bei dem Kommando on ( close ) . Hierbei ist es egal, ob das Kommando über das Modul oder die Fernbedienung ausgelöst wird.
  2066. </li><br>
  2067. <a name="operation_mode"></a>
  2068. <li>operation_mode<br>
  2069. Mode 0<br><br>
  2070. Dies ist der Standardmodus. In diesem Modus nutz das Modul nur den Kanal, der von der Fernbedienung vorgegeben ist. Hier kann es durch von FHEM verpasste Signale, Timingproblemen etc. im schlechtesten Fall zu falschen States und Positionsreadings kommen. Diese werden bei Anfahrt einer Endposition wieder synchronisiert.
  2071. <br><br>Mode 1<br><br>
  2072. Erweiterter Modus. In diesem Modus nutzt das Modul zwei Kan&auml;le. Den Standardkanal "channel" zum Empfangen der Fernbedienung. Dieser sollte nicht mehr durch das Rollo selbst empfangen werden. Und den "channel_send_mode_1", zum Senden an den Rollomotor. Hierzu ist eine Umkonfigurierung des Motors erforderlich. Dieser Modus ist in Bezug auf die Darstellung der States "deutlich sicherer", da ein Verpassen eines Signals durch FHEM nicht dazu f&uumlhrt, das falsche Positionen angezeigt werden. Das Rollo f&auml;hrt nur dann, wenn FHEM das Signal empfangen hat und an den Motor weiterreicht.
  2073. Eine Anleitung zur Konfiguration des Motors folgt.
  2074. </li><br>
  2075. <a name="time_down_to_favorite"></a>
  2076. <li>time_down_to_favorite<br>
  2077. beinhaltet die Fahrtzeit in Sekunden, die das Rollo von der 0% Position bis zur Hardware-Favoriten-Mittelposition ben&ouml;tigt. Diese Zeit muss manuell gemessen werden und eingetragen werden.
  2078. Ohne dieses Attribut ist das Modul nur eingeschr&auml;nkt funktionsf&auml;hig.</li><br>
  2079. <a name="time_to_close"></a>
  2080. <li>time_to_close<br>
  2081. beinhaltet die Fahrtzeit in Sekunden, die das Rollo von der 0% Position bis zur 100% Position ben&ouml;tigt. Diese Zeit muss manuell gemessen werden und eingetragen werden.
  2082. Ohne dieses Attribut ist das Modul nur eingeschr&auml;nkt funktionsf&auml;hig.</li><br>
  2083. <a name="time_to_open"></a>
  2084. <li>time_to_open<br>
  2085. beinhaltet die Fahrtzeit in Sekunden, die das Rollo von der 100% Position bis zur 0% Position ben&ouml;tigt. Diese Zeit muss manuell gemessen werden und eingetragen werden.
  2086. Ohne dieses Attribut ist das Modul nur eingeschr&auml;nkt funktionsf&auml;hig.</li><br>
  2087. <a name="prog_fav_sequence"></a>
  2088. <li>prog_fav_sequence<br>
  2089. beinhaltet die Kommandosequenz zum Programmieren der Harware-Favoritenposition</li><br>
  2090. <a name="debug_mode [0:1]"></a>
  2091. <li>debug_mode [0:1] <br>
  2092. Im Mode 1 werden zus&auml;tzliche Readings zur Fehlerbehebung angelegt, in denen die Ausgabe aller Modulelemente ausgegeben werden. Kommandos werden NICHT physisch gesendet.</li><br>
  2093. <a name="Info"></a>
  2094. <li>Info<br>
  2095. Die Attribute webcmd und devStateIcon werden beim Anlegen des Devices einmalig gesetzt und im auch im Betrieb an den jeweiligen Mode des Devices angepasst. Die Anpassung dieser Inhalte geschieht nur solange, bis diese durch den Nutzer ge&auml;ndert wurden. Danach erfolgt keine automatische Anpassung mehr.</li><br>
  2096. </ul>
  2097. </ul>
  2098. =end html_DE
  2099. =cut