98_DBPlan.pm 79 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153
  1. # $Id: 98_DBPlan.pm 80662 2018-02-23 18:53:00Z jowiemann $
  2. ##############################################################################
  3. #
  4. # 98_DBPlan.pm (Testversion)
  5. #
  6. # Calls the URL: https://reiseauskunft.bahn.de/bin/query.exe/dox?S=departure&Z=destination&start=1&rt=1
  7. # with the given attributes.
  8. # S=departure will be replace with "S=".AttrVal($name, "dbplan_station", undef)
  9. # Z=destination will be replace with "S=".AttrVal($name, "dbplan_destination", undef)
  10. # See also the domumentation for external calls
  11. # Internet-Reiseauskunft der Deutschen Bahn AG
  12. # Externe Aufrufparameter und Rückgabeparameter an externe Systeme
  13. ##############################################################################
  14. use strict;
  15. use warnings;
  16. use Time::HiRes qw(gettimeofday);
  17. use HttpUtils;
  18. use HTML::Entities;
  19. use LWP;
  20. use Digest::MD5 qw(md5_hex);
  21. my $note_index;
  22. sub DBPlan_Initialize($) {
  23. my ($hash) = @_;
  24. $hash->{DefFn} = 'DBPlan_Define';
  25. $hash->{UndefFn} = 'DBPlan_Undef';
  26. $hash->{SetFn} = 'DBPlan_Set';
  27. $hash->{GetFn} = 'DBPlan_Get';
  28. $hash->{AttrFn} = 'DBPlan_Attr';
  29. $hash->{ReadFn} = 'DBPlan_Read';
  30. $hash->{AttrList} =
  31. "dbplan_plan_url "
  32. . "dbplan_table_url "
  33. . "dbplan_station "
  34. . "dbplan_destination "
  35. . "dbplan_via_1 "
  36. . "dbplan_via_2 "
  37. . "dbplan_journey_prod:multiple-strict,Alle,ICE-Zuege,Intercity-Eurocityzuege,Interregio-Schnellzuege,Nahverkehr,"
  38. . "S-Bahnen,Busse,Schiffe,U-Bahnen,Strassenbahnen,Anruf-Sammeltaxi "
  39. . "dbplan_journey_opt:multiple-strict,Direktverbindung,Direktverbindung+Schlafwagen,Direktverbindung+Liegewagen,Fahrradmitnahme "
  40. . "dbplan_tariff_class:1,2 "
  41. . "dbplan_board_type:depart,arrive "
  42. . "dbplan_time_selection:arrive,depart "
  43. . "dbplan_delayed_Journey:off,on "
  44. . "dbplan_travel_date "
  45. . "dbplan_travel_time "
  46. . "dbplan_max_Journeys:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30 "
  47. . "dbplan_reg_train "
  48. . "dbplan_addon_options "
  49. . "dbplan-disable:0,1 "
  50. . "dbplan-remote-timeout "
  51. . "dbplan-remote-noshutdown:0,1 "
  52. . "dbplan-remote-buf:0,1 "
  53. . "dbplan-remote-loglevel:0,1,2,3,4,5 "
  54. . "dbplan-default-char "
  55. . "dbplan-table-headers "
  56. . "dbplan-station-file "
  57. . "dbplan-base-type:plan,table "
  58. . "dbplan-special-char-decode:none,utf8,latin1(default) "
  59. . "dbplan-reading-deselect:multiple-strict,plan_departure,plan_arrival,plan_connection,plan_departure_delay,plan_arrival_delay,"
  60. . "plan_travel_change,plan_travel_duration,travel_note,travel_note_text,travel_note_error,"
  61. . "travel_departure,travel_destination,travel_price "
  62. . $readingFnAttributes;
  63. }
  64. sub DBPlan_Define($$) {
  65. my ( $hash, $def ) = @_;
  66. my @a = split( "[ \t][ \t]*", $def );
  67. $hash->{version} = '23.02.2018 18:23:00';
  68. return "DBPlan_Define - too few parameters: define <name> DBPlan <interval> [<time offset>]" if( (@a < 3) || (@a > 4));
  69. my $name = $a[0];
  70. my $inter = 300;
  71. my $offset = 0;
  72. if(int(@a) == 3) {
  73. $inter = int($a[2]);
  74. if ($inter < 10 && $inter) {
  75. return "DBPlan_Define - interval too small, please use something > 10 (sec), default is 300 (sec)";
  76. }
  77. }
  78. elsif(int(@a) == 4) {
  79. $inter = int($a[2]);
  80. if ($inter < 10 && $inter) {
  81. return "DBPlan_Define - interval too small, please use something > 10 (sec), default is 300 (sec)";
  82. }
  83. $offset = int($a[3]);
  84. if ($offset < 0 ) {
  85. return "DBPlan_Define - time offset too small, please use something > 0 (min), default is 0 (min)";
  86. }
  87. }
  88. $hash->{Interval} = $inter;
  89. $hash->{Time_Offset} = $offset;
  90. Log3 $name, 3, "DBPlan_Define ($name) - defined with interval $hash->{Interval} (sec)";
  91. $hash->{PLAN_URL} = AttrVal($name, "dbplan_plan_url", 'https://reiseauskunft.bahn.de/bin/query.exe/dox?S=departure&Z=destination&start=1&rt=1');
  92. $hash->{TABLE_URL} = AttrVal($name, "dbplan_table_url", 'https://reiseauskunft.bahn.de/bin/bhftafel.exe/dox?&input=station&start=1&rt=1');
  93. $hash->{BASE_TYPE} = AttrVal($name, "dbplan-base-type", 'plan');
  94. Log3 $name, 3, "DBPlan_Define ($name) - defined with base type $hash->{BASE_TYPE}";
  95. $hash->{helper}{STATION} = AttrVal($name, "dbplan_station", undef);
  96. $hash->{helper}{DESTINATION} = AttrVal($name, "dbplan_destination", undef);
  97. # initial request after 2 secs, there timer is set to interval for further update
  98. my $nt = gettimeofday()+$hash->{Interval};
  99. $hash->{TRIGGERTIME} = $nt;
  100. $hash->{TRIGGERTIME_FMT} = FmtDateTime($nt);
  101. RemoveInternalTimer($hash);
  102. InternalTimer($nt, "DBPlan_Get_DB_Info", $hash, 0);
  103. unless(defined($hash->{helper}{STATION}) || (defined($hash->{helper}{DESTINATION}) && $hash->{BASE_TYPE} eq "plan"))
  104. {
  105. $hash->{DevState} = 'defined';
  106. #$hash->{state} = 'defined';
  107. readingsSingleUpdate($hash, "state", "defined", 1);
  108. return undef;
  109. }
  110. # unless(defined($hash->{helper}{DESTINATION}) && $hash->{BASE_TYPE} eq "plan")
  111. # {
  112. # $hash->{DevState} = 'defined';
  113. # $hash->{state} = 'defined';
  114. # readingsSingleUpdate($hash, "state", "defined", 1);
  115. # return undef;
  116. # }
  117. $hash->{DevState} = 'initialized';
  118. #$hash->{state} = 'initialized';
  119. readingsSingleUpdate($hash, "state", "initialized", 1);
  120. return undef;
  121. }
  122. sub DBPlan_Undef($$) {
  123. my ($hash, $arg) = @_;
  124. my $name = $hash->{NAME};
  125. RemoveInternalTimer($hash);
  126. Log3 $name, 3, "DBPlan_Undef ($name) - removed";
  127. return undef;
  128. }
  129. sub DBPlan_Get($@) {
  130. my ($hash, @arguments) = @_;
  131. my $name = $hash->{NAME};
  132. return "argument missing" if(int(@arguments) < 2);
  133. if($arguments[1] eq "searchStation" and int(@arguments) >= 2)
  134. {
  135. my $table = "";
  136. my $head = "Station Id";
  137. my $width = 10;
  138. my $stext = join '.*?', @arguments[2..$#arguments];
  139. $stext = DBPlan_html2txt($stext);
  140. if($stext ne "") {
  141. $stext = $stext . ".*?";
  142. }
  143. foreach my $stationNames (sort keys %{$hash->{helper}{STATION_NAMES}})
  144. {
  145. my $string = $stationNames ." - ".$hash->{helper}{STATION_NAMES}{$stationNames};
  146. my $test = DBPlan_html2txt($string);
  147. if($test =~ m/$stext/si) {
  148. $width = length($string) if(length($string) > $width);
  149. $table .= $string."\n";
  150. }
  151. }
  152. return $head."\n".("-" x $width)."\n".encode_entities($table);
  153. }
  154. # elsif($arguments[1] eq "showStations" and exists($hash->{helper}{STATION_NAMES}))
  155. # {
  156. # my $table = "";
  157. # my $head = "Station Id";
  158. # my $width = 10;
  159. #
  160. # foreach my $stationNames (sort keys %{$hash->{helper}{STATION_NAMES}})
  161. # {
  162. # my $string = $stationNames ." - ".$hash->{helper}{STATION_NAMES}{$stationNames};
  163. # $width = length($string) if(length($string) > $width);
  164. # $table .= $string."\n";
  165. # }
  166. #
  167. # return $head."\n".("-" x $width)."\n".encode_entities($table);
  168. # }
  169. elsif($arguments[1] eq "PlainText")
  170. {
  171. my $table = "";
  172. my $head = "";
  173. my $width = 100;
  174. $hash->{helper}{plain} = 1;
  175. $table = DBPlan_Get_DB_Plain_Text($hash);
  176. delete ($hash->{helper}{plain}) if exists($hash->{helper}{plain});
  177. return $head."\n".("-" x $width)."\n".$table;
  178. }
  179. else
  180. {
  181. return "unknown argument ".$arguments[1].", choose one of".(exists($hash->{helper}{STATION_NAMES}) ? " searchStation" : "")." PlainText";
  182. }
  183. }
  184. sub DBPlan_Set($@) {
  185. my ($hash, $name, $cmd, @val) = @_;
  186. my $list = "interval";
  187. $list .= " timeOffset";
  188. $list .= " rereadStationFile:noArg" if(defined(AttrVal($name, "dbplan-station-file", undef)));
  189. $list .= " rereadDBInfo:noArg" if($hash->{DevState} ne 'disabled' && $hash->{DevState} ne 'defined');
  190. $list .= " inactive:noArg" if($hash->{DevState} eq 'active' || $hash->{DevState} eq 'initialized');
  191. $list .= " active:noArg" if($hash->{DevState} eq 'inactive');
  192. if ($cmd eq 'interval')
  193. {
  194. if (int @val == 1 && $val[0] > 10)
  195. {
  196. $hash->{Interval} = $val[0];
  197. # initial request after 2 secs, there timer is set to interval for further update
  198. my $nt = gettimeofday()+$hash->{Interval};
  199. $hash->{TRIGGERTIME} = $nt;
  200. $hash->{TRIGGERTIME_FMT} = FmtDateTime($nt);
  201. if($hash->{DevState} eq 'active' || $hash->{DevState} eq 'initialized') {
  202. RemoveInternalTimer($hash);
  203. InternalTimer($nt, "DBPlan_Get_DB_Info", $hash, 0);
  204. Log3 $name, 3, "DBPlan_Set ($name) - restarted with new timer interval $hash->{Interval} (sec)";
  205. } else {
  206. Log3 $name, 3, "DBPlan_Set ($name) - new timer interval $hash->{Interval} (sec) will be active when starting/enabling";
  207. }
  208. return undef;
  209. } elsif (int @val == 1 && $val[0] <= 10) {
  210. Log3 $name, 3, "DBPlan_Set ($name) - interval: $val[0] (sec) to small, continuing with $hash->{Interval} (sec)";
  211. return "DBPlan_Set - interval too small, please use something > 10, defined is $hash->{Interval} (sec)";
  212. } else {
  213. Log3 $name, 3, "DBPlan_Set ($name) - interval: no interval (sec) defined, continuing with $hash->{Interval} (sec)";
  214. return "DBPlan_Set - no interval (sec) defined, please use something > 10, defined is $hash->{Interval} (sec)";
  215. }
  216. } # if interval
  217. elsif ($cmd eq 'rereadDBInfo')
  218. {
  219. DBPlan_Get_DB_Info($hash);
  220. return undef;
  221. }
  222. elsif ($cmd eq 'timeOffset')
  223. {
  224. if (int @val == 1 && $val[0] >= 0)
  225. {
  226. $hash->{Time_Offset} = $val[0];
  227. DBPlan_Get_DB_Info($hash);
  228. return undef;
  229. } elsif (int @val == 1 && $val[0] < 0) {
  230. Log3 $name, 3, "DBPlan_Set ($name) - Time_Offset: $val[0] (min) to small, continuing with $hash->{Time_Offset} (min)";
  231. return "DBPlan_Set - time offset too small, please use something > 10, defined is $hash->{Time_Offset} (sec)";
  232. } else {
  233. Log3 $name, 3, "DBPlan_Set ($name) - Time_Offset: no time offset (min) defined, continuing with $hash->{Time_Offset} (min)";
  234. return "DBPlan_Set - no time offset (min) defined, please use something > 10, defined is $hash->{Time_Offset} (sec)";
  235. }
  236. }
  237. elsif ($cmd eq 'inactive')
  238. {
  239. $hash->{DevState} = 'inactive';
  240. #$hash->{state} = 'inactive';
  241. readingsSingleUpdate($hash, "state", "inactive", 1);
  242. RemoveInternalTimer($hash);
  243. Log3 $name, 3, "DBPlan_Set ($name) - interval timer set to inactive";
  244. return undef;
  245. } # if stop
  246. elsif ($cmd eq 'active')
  247. {
  248. RemoveInternalTimer($hash);
  249. InternalTimer(gettimeofday()+2, "DBPlan_Get_DB_Info", $hash, 0) if ($hash->{DevState} eq "inactive");
  250. $hash->{DevState}='initialized';
  251. #$hash->{state}='initialized';
  252. readingsSingleUpdate($hash, "state", "initialized", 1);
  253. Log3 $name, 3, "DBPlan_Set ($name) - interval timer started with interval $hash->{Interval} (sec)";
  254. return undef;
  255. } # if start
  256. elsif($cmd eq "rereadStationFile")
  257. {
  258. DBPlan_loadStationFile($hash);
  259. return undef;
  260. }
  261. return "DBPlan_Set ($name) - Unknown argument $cmd or wrong parameter(s), choose one of $list";
  262. }
  263. sub DBPlan_Attr(@) {
  264. my ($cmd,$name,$attrName,$attrVal) = @_;
  265. my $hash = $defs{$name};
  266. $attrVal = "not defined" unless(defined($attrVal));
  267. # $cmd can be "del" or "set"
  268. # $name is device name
  269. # attrName and attrVal are Attribute name and value
  270. if ($cmd eq "set") {
  271. if ($attrName eq "dbplan-travel-date") {
  272. if (!($attrVal =~ m/(0[1-9]|1[0-9]|2[0-9]|3[01]).(0[1-9]|1[012]).\d\d/s ) ) {
  273. Log3 $name, 3, "DBPlan_Attr ($name) - $attrVal is a wrong date";
  274. return ("DBPlan_Attr: $attrVal is a wrong date. Format is dd.mm.yy");
  275. }
  276. } elsif ($attrName eq "dbplan-travel-time") {
  277. if (!($attrVal =~ m/(?:0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]/s)) {
  278. Log3 $name, 3, "DBPlan_Attr ($name) - $attrVal is a wrong time";
  279. return ("DBPlan_Attr: $attrVal is a wrong time. Format is hh:mm");
  280. }
  281. }
  282. }
  283. if ($attrName eq "dbplan-disable") {
  284. if($cmd eq "set") {
  285. if($attrVal eq "0") {
  286. RemoveInternalTimer($hash);
  287. InternalTimer(gettimeofday()+2, "DBPlan_Get_DB_Info", $hash, 0); # if ($hash->{DevState} eq "disabled");
  288. $hash->{DevState}='initialized';
  289. #$hash->{state}='initialized';
  290. readingsSingleUpdate($hash, "state", "initialized", 1);
  291. Log3 $name, 3, "DBPlan_Attr ($name) - interval timer enabled with interval $hash->{Interval} (sec)";
  292. } else {
  293. $hash->{DevState} = 'disabled';
  294. #$hash->{state} = 'disabled';
  295. readingsSingleUpdate($hash, "state", "disabled", 1);
  296. RemoveInternalTimer($hash);
  297. Log3 $name, 3, "DBPlan_Attr ($name) - interval timer disabled";
  298. }
  299. $attr{$name}{$attrName} = $attrVal;
  300. } elsif ($cmd eq "del") {
  301. RemoveInternalTimer($hash);
  302. InternalTimer(gettimeofday()+2, "DBPlan_Get_DB_Info", $hash, 0) if ($hash->{DevState} eq "disabled");
  303. $hash->{DevState}='initialized';
  304. #$hash->{state}='initialized';
  305. readingsSingleUpdate($hash, "state", "initialized", 1);
  306. Log3 $name, 3, "DBPlan_Attr ($name) - interval timer enabled with interval $hash->{Interval} (sec)";
  307. }
  308. } elsif ($attrName eq "dbplan_plan_url") {
  309. if($cmd eq "set") {
  310. $hash->{PLAN_URL} = $attrVal;
  311. $attr{$name}{$attrName} = $attrVal;
  312. } elsif ($cmd eq "del") {
  313. $hash->{PLAN_URL} = 'https://reiseauskunft.bahn.de/bin/query.exe/dox?S=departure&Z=destination&start=1&rt=1';
  314. }
  315. Log3 $name, 3, "DBPlan_Attr ($name) - url set to " . $hash->{PLAN_URL};
  316. } elsif ($attrName eq "dbplan_table_url") {
  317. if($cmd eq "set") {
  318. $hash->{TABLE_URL} = $attrVal;
  319. $attr{$name}{$attrName} = $attrVal;
  320. } elsif ($cmd eq "del") {
  321. $hash->{TABLE_URL} = 'https://reiseauskunft.bahn.de/bin/bhftafel.exe/dox?&rt=1&input=station';
  322. }
  323. Log3 $name, 3, "DBPlan_Attr ($name) - url set to " . $hash->{TABLE_URL};
  324. } elsif ($attrName eq "dbplan_station") {
  325. if($cmd eq "set") {
  326. $hash->{helper}{STATION} = $attrVal;
  327. $attr{$name}{$attrName} = $attrVal;
  328. if(!defined($hash->{helper}{DESTINATION}) && ($hash->{BASE_TYPE} eq "plan")) {
  329. $hash->{DevState} = 'defined';
  330. #$hash->{state} = 'defined';
  331. readingsSingleUpdate($hash, "state", "defined", 1);
  332. } else {
  333. $hash->{DevState} = 'initialized';
  334. #$hash->{state} = 'initialized';
  335. readingsSingleUpdate($hash, "state", "initialized", 1);
  336. }
  337. Log3 $name, 3, "DBPlan_Attr ($name) - station set to " . $hash->{helper}{STATION};
  338. } elsif($cmd eq "del") {
  339. delete($hash->{helper}{STATION}) if(defined($hash->{helper}{STATION}));
  340. $hash->{DevState} = 'defined';
  341. #$hash->{state} = 'defined';
  342. readingsSingleUpdate($hash, "state", "defined", 1);
  343. Log3 $name, 3, "DBPlan_Attr ($name) - deleted $attrName : $attrVal";
  344. }
  345. } elsif ($attrName eq "dbplan_destination") {
  346. if($cmd eq "set") {
  347. $hash->{helper}{DESTINATION} = $attrVal;
  348. $attr{$name}{$attrName} = $attrVal;
  349. unless(defined($hash->{helper}{STATION})) {
  350. $hash->{DevState} = 'defined';
  351. #$hash->{state} = 'defined';
  352. readingsSingleUpdate($hash, "state", "defined", 1);
  353. } else {
  354. $hash->{DevState} = 'initialized';
  355. #$hash->{state} = 'initialized';
  356. readingsSingleUpdate($hash, "state", "initialized", 1);
  357. }
  358. Log3 $name, 3, "DBPlan_Attr ($name) - destination set to " . $hash->{helper}{DESTINATION};
  359. } elsif ($cmd eq "del") {
  360. if(($hash->{BASE_TYPE} eq "table") && defined($hash->{helper}{STATION})) {
  361. $hash->{DevState} = 'initialized';
  362. #$hash->{state} = 'initialized';
  363. readingsSingleUpdate($hash, "state", "initialized", 1);
  364. } else {
  365. $hash->{DevState} = 'defined';
  366. #$hash->{state} = 'defined';
  367. readingsSingleUpdate($hash, "state", "defined", 1);
  368. }
  369. delete($hash->{helper}{DESTINATION}) if(defined($hash->{helper}{DESTINATION}));
  370. Log3 $name, 3, "DBPlan_Attr ($name) - deleted $attrName : $attrVal";
  371. }
  372. } elsif ($attrName eq "dbplan-station-file") {
  373. if($cmd eq "set") {
  374. return DBPlan_loadStationFile($hash, $attrVal);
  375. } elsif ($cmd eq "del") {
  376. delete($hash->{helper}{STATION_NAMES}) if(defined($hash->{helper}{STATION_NAMES}));
  377. Log3 $name, 3, "DBPlan_Attr ($name) - deleted $attrName : $attrVal";
  378. }
  379. } elsif ($attrName eq "dbplan-base-type") {
  380. if($cmd eq "set") {
  381. $hash->{BASE_TYPE} = $attrVal;
  382. $attr{$name}{$attrName} = $attrVal;
  383. RemoveInternalTimer($hash);
  384. InternalTimer(gettimeofday()+2, "DBPlan_Get_DB_Info", $hash, 0);
  385. $hash->{DevState}='initialized';
  386. #$hash->{state}='initialized';
  387. readingsSingleUpdate($hash, "state", "initialized", 1);
  388. } elsif ($cmd eq "del") {
  389. $hash->{BASE_TYPE} = 'plan';
  390. }
  391. my $ret;
  392. $ret = fhem("deletereading $name table.*", 1);
  393. $ret = fhem("deletereading $name plan.*", 1);
  394. $ret = fhem("deletereading $name travel.*", 1);
  395. Log3 $name, 3, "DBPlan_Attr ($name) - base type set to " . $hash->{BASE_TYPE};
  396. } else {
  397. if($cmd eq "set") {
  398. $attr{$name}{$attrName} = $attrVal;
  399. Log3 $name, 3, "DBPlan_Attr ($name) - set $attrName : $attrVal";
  400. } elsif ($cmd eq "del") {
  401. Log3 $name, 3, "DBPlan_Attr ($name) - deleted $attrName : $attrVal";
  402. }
  403. }
  404. return undef;
  405. }
  406. ############################################
  407. # Calculate delay difference in minutes
  408. #
  409. sub DBPlan_getMinutesDiff
  410. {
  411. my ($start, $end) = @_;
  412. my ($hourStart, $minuteStart) = $start =~ m|(\d{2}):(\d{2})|;
  413. my ($hourEnd, $minuteEnd) = $end =~ m|(\d{2}):(\d{2})|;
  414. # invalid time?
  415. return 0 if ($hourStart eq "" || $minuteStart eq "" || $hourEnd eq "" || $minuteEnd eq "");
  416. my $totalMinutesStart = ($hourStart * 60) + $minuteStart;
  417. my $totalMinutesEnd = ($hourEnd * 60) + $minuteEnd;
  418. my $diff = 0;
  419. # midnight?
  420. if ($totalMinutesEnd < $totalMinutesStart)
  421. {
  422. # 24:00 - start
  423. $diff = (24 * 60) - $totalMinutesStart;
  424. $diff = $diff + $totalMinutesEnd;
  425. }
  426. else
  427. {
  428. $diff = $totalMinutesEnd - $totalMinutesStart;
  429. }
  430. return $diff;
  431. }
  432. #####################################
  433. # generating bit pattern for DB products
  434. #
  435. # Bit Nummer Produktklasse
  436. # 0 ICE-Züge
  437. # 1 Intercity- und Eurocityzüge
  438. # 2 Interregio- und Schnellzüge
  439. # 3 Nahverkehr, sonstige Züge
  440. # 4 S-Bahnen
  441. # 5 Busse
  442. # 6 Schiffe
  443. # 7 U-Bahnen
  444. # 8 Straßenbahnen
  445. # 9 Anruf Sammeltaxi
  446. #
  447. sub DBPlan_products($)
  448. {
  449. my ($hash) = @_;
  450. my $name = $hash->{NAME};
  451. my @prod_list = split("(,|\\|)", AttrVal($name, "dbplan_journey_prod", "none"));
  452. my $products = 0;
  453. return $products if((grep { /^(Alle)$/ } @prod_list));
  454. $products += 1 if((grep { /^(ICE-Zuege)$/ } @prod_list));
  455. $products += 2 if((grep { /^(Intercity-Eurocityzuege)$/ } @prod_list));
  456. $products += 4 if((grep { /^(Interregio-Schnellzuege)$/ } @prod_list));
  457. $products += 8 if((grep { /^(Nahverkehr)$/ } @prod_list));
  458. $products += 16 if((grep { /^(S-Bahnen)$/ } @prod_list));
  459. $products += 32 if((grep { /^(Busse)$/ } @prod_list));
  460. $products += 64 if((grep { /^(Schiffe)$/ } @prod_list));
  461. $products += 128 if((grep { /^(U-Bahnen)$/ } @prod_list));
  462. $products += 256 if((grep { /^(Strassenbahnen)$/ } @prod_list));
  463. $products += 512 if((grep { /^(Anruf-Sammeltaxi)$/ } @prod_list));
  464. return $products;
  465. }
  466. #####################################
  467. # generating bit pattern DB options
  468. sub DBPlan_options($)
  469. {
  470. my ($hash) = @_;
  471. my $name = $hash->{NAME};
  472. my @opt_list = split("(,|\\|)", AttrVal($name, "dbplan_journey_opt", "none"));
  473. my $options = 0;
  474. $options += 1 if((grep { /^(Direktverbindung)$/ } @opt_list));
  475. $options += 2 if((grep { /^(Direktverbindung+Schlafwagen)$/ } @opt_list));
  476. $options += 4 if((grep { /^(Direktverbindung+Liegewagen)$/ } @opt_list));
  477. $options += 8 if((grep { /^(Fahrradmitnahme)$/ } @opt_list));
  478. return $options;
  479. }
  480. #####################################
  481. # generating url with defined options
  482. sub DBPlan_make_url($) {
  483. my ($hash) = @_;
  484. my $name = $hash->{NAME};
  485. my $plan_url = "";
  486. my $oTmp;
  487. my @prod_list = split("(,|\\|)", AttrVal($name, "dbplan_journey_prod", "none"));
  488. my @opt_list = split("(,|\\|)", AttrVal($name, "dbplan_journey_opt", "none"));
  489. my $products = DBPlan_products($hash);
  490. my $options = DBPlan_options($hash);
  491. my $station = $hash->{helper}{STATION};
  492. $station =~ s/ /+/g;
  493. if($hash->{BASE_TYPE} eq "plan") {
  494. $plan_url = $hash->{PLAN_URL};
  495. $plan_url =~ s/departure/$station/;
  496. $oTmp = $hash->{helper}{DESTINATION};
  497. $oTmp =~ s/ /+/g;
  498. $plan_url =~ s/destination/$oTmp/;
  499. $oTmp = AttrVal($name, "dbplan_via_1", "");
  500. $plan_url .= '&V1='.$oTmp if($oTmp ne "");
  501. $oTmp = AttrVal($name, "dbplan_via_2", "");
  502. $plan_url .= '&V2='.$oTmp if($oTmp ne "");
  503. $oTmp = AttrVal($name, "dbplan_tariff_class", "");
  504. $plan_url .= '&tariffClass='.$oTmp if($oTmp ne "");
  505. } else {
  506. $plan_url = $hash->{TABLE_URL};
  507. $plan_url =~ s/station/$station/;
  508. $oTmp = AttrVal($name, "dbplan_reg_train", "");
  509. $oTmp =~ s/ /+/g;
  510. $plan_url .= '&REQTrain_name=' . $oTmp;
  511. $oTmp = AttrVal($name, "dbplan_delayed_Journey", "off");
  512. $oTmp .= '&delayedJourney=' . $oTmp if($oTmp ne "off");
  513. $oTmp = AttrVal($name, "dbplan_max_Journeys", "off");
  514. $plan_url .= '&maxJourneys=' . $oTmp;
  515. $plan_url .= '&boardType=' . substr(AttrVal($name, "dbplan_board_type", "depart"),0,3);
  516. }
  517. $plan_url .= '&journeyProducts='.$products if($products > 0);
  518. $plan_url .= '&journeyOptions='.$options if($options > 0);
  519. my $travel_date = AttrVal($name, "dbplan_travel_date", "");
  520. my $travel_time = AttrVal($name, "dbplan_travel_time", "");
  521. my $time_sel = AttrVal($name, "dbplan_time_selection", "depart");
  522. $plan_url .= '&date='.$travel_date if($travel_date ne "");
  523. if($travel_time ne "") {
  524. $plan_url .= '&time='.$travel_time;
  525. } elsif ( $hash->{Time_Offset} > 0 ) {
  526. $plan_url .= '&time='.strftime( "%H:%M", localtime(time+60*$hash->{Time_Offset}));
  527. }
  528. if($travel_date ne "" || $travel_time ne "") {
  529. $plan_url .= '&timesel='.$time_sel if($hash->{BASE_TYPE} eq "plan");
  530. }
  531. $oTmp = AttrVal($name, "dbplan_addon_options", "");
  532. $plan_url .= $oTmp if($oTmp ne "");
  533. $plan_url .= '&'; # see parameter description
  534. if (exists($hash->{helper}{plain})) {
  535. $plan_url =~ s/dox/dl/g;
  536. }
  537. Log3 $name, 4, "DBPlan ($name) - DB timetable: calling url: $plan_url";
  538. return ($plan_url);
  539. }
  540. #####################################
  541. # Getting the DB main stationtable
  542. sub DBPlan_Get_DB_Plain_Text($) {
  543. my ($hash) = @_;
  544. my $name = $hash->{NAME};
  545. my $param;
  546. $param->{url} = DBPlan_make_url($hash);
  547. $param->{noshutdown} = AttrVal($name, "dbplan-remote-noshutdown", 1);
  548. $param->{timeout} = AttrVal($name, "dbplan-remote-timeout", 5);
  549. $param->{loglevel} = AttrVal($name, "dbplan-remote-loglevel", 4);
  550. $param->{buf} = AttrVal($name, "dbplan-remote-loglevel", 1);
  551. Log3 $name, 4, "DBPlan ($name) - Get_DB_Plain_Textget: DB plain text info";
  552. my ($err, $data) = HttpUtils_BlockingGet($param);
  553. Log3 $name, 5, "DBPlan ($name) - Get_DB_Plain_Text: received http response code ".$param->{code} if(exists($param->{code}));
  554. if ($err ne "")
  555. {
  556. Log3 $name, 3, "DBPlan ($name) - Get_DB_Plain_Text: got error while requesting DB info: $err";
  557. return "got error while requesting DB info: $err";
  558. }
  559. if($data eq "" and exists($param->{code}))
  560. {
  561. Log3 $name, 3, "DBPlan ($name) - Get_DB_Plain_Text: received http code ".$param->{code}." without any data after requesting DB plain text";
  562. return "received no data after requesting DB plain text";
  563. }
  564. my $pattern = '\<PRE\>(.*?)\<\/PRE\>';
  565. Log3 $name, 5, "DBPlan ($name) - $data";
  566. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Stationtable for plain text: finished";
  567. if ($data =~ m/$pattern/is) {
  568. return $1;
  569. } else {
  570. return ( "no information found" );
  571. }
  572. return ($data);
  573. }
  574. #####################################
  575. # Getting the DB main stationtable
  576. sub DBPlan_Get_DB_Info($)
  577. {
  578. my ($hash) = @_;
  579. my $name = $hash->{NAME};
  580. if($hash->{DevState} eq 'active' || $hash->{DevState} eq 'initialized' || $hash->{DevState} eq 'defined') {
  581. my $nt = gettimeofday()+$hash->{Interval};
  582. $hash->{TRIGGERTIME} = $nt;
  583. $hash->{TRIGGERTIME_FMT} = FmtDateTime($nt);
  584. RemoveInternalTimer($hash);
  585. InternalTimer($nt, "DBPlan_Get_DB_Info", $hash, 1) if (int($hash->{Interval}) > 0);
  586. Log3 $name, 5, "DBPlan ($name) - DBPlan_Get_DB_Info: restartet InternalTimer with $hash->{Interval}";
  587. }
  588. unless(defined($hash->{helper}{STATION}))
  589. {
  590. Log3 $name, 3, "DBPlan ($name) - DBPlan_Get_DB_Info: no valid station defined";
  591. return;
  592. }
  593. if($hash->{BASE_TYPE} eq "plan") {
  594. unless(defined($hash->{helper}{DESTINATION}))
  595. {
  596. Log3 $name, 3, "DBPlan ($name) - Get_DB_Info: no valid destination defined";
  597. return;
  598. }
  599. $hash->{callback} = \&DBPlan_Parse_Timetable;
  600. } else {
  601. $hash->{callback} = \&DBPlan_Parse_Stationtable;
  602. }
  603. $hash->{url} = DBPlan_make_url($hash); #$plan_url;
  604. $hash->{noshutdown} = AttrVal($name, "dbplan-remote-noshutdown", 1);
  605. $hash->{timeout} = AttrVal($name, "dbplan-remote-timeout", 5);
  606. $hash->{loglevel} = AttrVal($name, "dbplan-remote-loglevel", 4);
  607. $hash->{buf} = AttrVal($name, "dbplan-remote-loglevel", 1);
  608. Log3 $name, 4, "DBPlan ($name) - DBPlan_Get_DB_Info: next getting $hash->{url}";
  609. HttpUtils_NonblockingGet($hash);
  610. return undef;
  611. }
  612. #####################################
  613. # Parsing the DB main station table
  614. #
  615. sub DBPlan_Parse_Stationtable($)
  616. {
  617. my ($hash, $err, $data) = @_;
  618. my $name = $hash->{NAME};
  619. delete($hash->{error}) if(exists($hash->{error}));
  620. my $ret = fhem("deletereading $name table_.*", 1);
  621. if ($err) {
  622. Log3 $name, 3, "DBPlan ($name) - DBPlan_Parse_Stationtable: got error in callback: $err";
  623. return undef;
  624. }
  625. if($data eq "")
  626. {
  627. Log3 $name, 3, "DBPlan ($name) - DBPlan_Parse_Stationtable: received http without any data after requesting DB stationtable";
  628. return undef;
  629. }
  630. Log3 $name, 5, "DBPlan ($name) - DBPlan_Parse_Stationtable: Callback called with Hash: $hash, data: $data\r\n";
  631. if(AttrVal($name, "verbose", 3) >= 5) {
  632. $hash->{Stationtable} = $data;
  633. }
  634. ##################################################################################
  635. # only for debugging
  636. if(AttrVal($name, "verbose", 3) >= 5) {
  637. readingsBeginUpdate($hash);
  638. readingsBulkUpdate( $hash, "dbg_db_plan", $data );
  639. readingsEndUpdate( $hash, 1 );
  640. }
  641. my $i;
  642. my $defChar = AttrVal($name, "dbplan-default-char", "delete");
  643. my $pattern = '';
  644. if ($data =~ m/\<div.class="haupt errormsg"\>(.*?)\<\/div\>/s) {
  645. Log3 $name, 3, "DBPlan ($name) - error in DB request. Bitte Log prüfen.";
  646. readingsBeginUpdate($hash);
  647. readingsBulkUpdate( $hash, "table_error", "error in DB request: " . DBPlan_decode(DBPlan_html2uml($1)) );
  648. readingsEndUpdate( $hash, 1 );
  649. $pattern = '\<title\>Deutsche Bahn - Abfahrt\<\/title\>(.*?)\<p class="webtrack"\>';
  650. # if ($data =~ m/$pattern/s) {
  651. # my $error_text = DBPlan_html2txt($1);
  652. # Log3 $name, 3, "DBPlan ($name) - DBPlan_Parse_Stationtable: error description of DB stationtable request: $error_text";
  653. # }
  654. return undef;
  655. }
  656. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Stationtable: successfully identified";
  657. ##################################################################################
  658. # Parsing connection table.
  659. $pattern = '\<div.class="sqdetails.*?trow"\>.*?\<\/div\>';
  660. my $count = 0;
  661. my $index = "";
  662. my $btype = AttrVal($name, "dbplan_board_type", "depart") . "_";
  663. foreach my $line ($data =~ m/$pattern/gs) {
  664. $count ++;
  665. $index = $btype . sprintf("%02d", $count);
  666. ##################################################################################
  667. # only for debugging
  668. if(AttrVal($name, "verbose", 3) >= 5) {
  669. readingsBeginUpdate($hash);
  670. readingsBulkUpdate( $hash, "dbg_connect_table_$index", $line ) if(defined($line));
  671. readingsEndUpdate( $hash, 1 );
  672. }
  673. $line =~ s/\x0a//g;
  674. Log3 $name, 5, "$line";
  675. # $pattern = '<span class="bold">S 13</span></a>&gt;&gt;Sindorf<br /><span class="bold">15:34</span>&nbsp;<span class="okmsg">+2</span></span>,&nbsp;&nbsp;Gl. 2</div>';
  676. my $table_row = "";
  677. # Zug
  678. $pattern = '\<span.class="bold"\>(.*?)\<\/span\>';
  679. if ($line =~ m/$pattern/s) {
  680. $table_row .= $1;
  681. }
  682. # nächster Bahnhof
  683. $pattern = '\<\/span\>\<\/a\>&gt;&gt;(.*?)\<br.\/\>\<span.class';
  684. if ($line =~ m/$pattern/s) {
  685. $table_row .= "|" . $1;
  686. }
  687. # Uhrzeit ohne Verspätung
  688. $pattern = '\<br.\/\>\<span.class="bold"\>(.*?)\<\/span\>\<\/div\>';
  689. if ($line =~ m/$pattern/s) {
  690. $table_row .= "|" . $1;
  691. }
  692. # Uhrzeit mit Verspätung
  693. $pattern = '\<br.\/\>\<span.class="bold"\>(.*?)\<\/span\>&nbsp;';
  694. if ($line =~ m/$pattern/s) {
  695. $table_row .= "|" . $1;
  696. }
  697. # Verspätung
  698. $pattern = '\<span.class="okmsg"\>(.*?)\<\/span\>';
  699. if ($line =~ m/$pattern/s) {
  700. $table_row .= "|" . $1;
  701. } else {
  702. $table_row .= "|-";
  703. }
  704. # Verspätung rot
  705. $pattern = '\<span.class="red"\>(.*?)\<\/span\>,&nbsp;&nbsp;';
  706. if ($line =~ m/$pattern/s) {
  707. $table_row .= "|" . $1;
  708. } else {
  709. $table_row .= "|-";
  710. }
  711. # Gleis
  712. $pattern = '&nbsp;&nbsp;(.*?)\<\/div\>';
  713. my $pattern1 = '&nbsp;&nbsp;(.*?),\<br\/\>\<a.class="red.underline"';
  714. my $pattern2 = '&nbsp;&nbsp;(.*?)\<br.\/\>\<span.class="red"\>.*?\<\/span\>\<br.\/\>';
  715. if ($line =~ m/$pattern1/s) {
  716. $table_row .= "|" . $1;
  717. } elsif ($line =~ m/$pattern2/s) {
  718. $table_row .= "|" . $1;
  719. } else {
  720. if ($line =~ m/$pattern/s) {
  721. $table_row .= "|" . $1;
  722. } else {
  723. $table_row .= "|-";
  724. }
  725. }
  726. # Hinweise
  727. $pattern1 = '\<br\/\>\<a.class="red.underline".*?\<span.class="red"\>(.*?)\<\/span\>\<\/a\>\<\/span\>';
  728. $pattern2 = '\<br.\/\>\<span.class="red.*?">(.*?)\<\/span>\<br.\/\>\<span.class="red.*?"\>(.*?)\<\/span\>\<\/div\>';
  729. if ($line =~ m/$pattern1/s) {
  730. # Ersatzfahrt&nbsp;ICE 2555
  731. $table_row .= "|" . $1;
  732. } elsif ($line =~ m/$pattern2/s) {
  733. $table_row .= "|" . $1 . " " . $2;
  734. } else {
  735. $table_row .= "|-";
  736. }
  737. my $convChar = AttrVal($name, "dbplan-special-char-decode", "latin1(default)");
  738. if($convChar eq "latin1(default)"){
  739. $table_row = DBPlan_html2uml($table_row);
  740. }
  741. if($convChar eq "utf8"){
  742. $table_row = DBPlan_decode(DBPlan_html2uml($table_row));
  743. }
  744. readingsBeginUpdate($hash);
  745. readingsBulkUpdate( $hash, "table_$index", $table_row );
  746. readingsEndUpdate( $hash, 1 );
  747. }
  748. unless($count) {
  749. Log3 $name, 3, "DBPlan ($name) - DBPlan_Parse_Stationtable: no station table found";
  750. readingsBeginUpdate($hash);
  751. readingsBulkUpdate( $hash, "table_row_cnt", "0" );
  752. readingsEndUpdate( $hash, 1 );
  753. } else {
  754. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Stationtable: table plans read successfully";
  755. if($hash->{DevState} eq 'initialized' || $hash->{DevState} eq 'inactive') {
  756. $hash->{DevState}='active' ;
  757. #$hash->{state}='active';
  758. readingsSingleUpdate($hash, "state", "active", 1);
  759. }
  760. readingsBeginUpdate($hash);
  761. readingsBulkUpdate( $hash, "table_row_cnt", sprintf("%02d", $count));
  762. readingsEndUpdate( $hash, 1 );
  763. }
  764. return undef;
  765. }
  766. #####################################
  767. # Parsing the DB travel notes
  768. sub DBPlan_Parse_Travel_Notes($)
  769. {
  770. my ($hash, $err, $data) = @_;
  771. my $name = $hash->{NAME};
  772. my $index = $hash->{helper}{note_index};
  773. my $h_name = "delay_" . $index;
  774. my ($d_dly, $a_dly) = split(",", $hash->{helper}{$h_name});
  775. my $error = 0;
  776. if ($err) {
  777. Log3 $name, 3, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): got error in callback: $err";
  778. $error = 1;
  779. }
  780. if($data eq "")
  781. {
  782. Log3 $name, 3, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): received http without any data after requesting DB travel notes";
  783. $error = 2;
  784. }
  785. if($error) {
  786. ##################################################################################
  787. # Recursiv call until index = 0
  788. $index -= 1;
  789. if($index <= 0) {
  790. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): parsed all notes";
  791. return;
  792. }
  793. my $note_text = "travel_note_link_$index";
  794. $hash->{url} = $hash->{helper}{$note_text};
  795. $hash->{helper}{note_index} = $index;
  796. $hash->{callback} = \&DBPlan_Parse_Travel_Notes;
  797. $hash->{noshutdown} = AttrVal($name, "dbplan-remote-noshutdown", 1);
  798. $hash->{timeout} = AttrVal($name, "dbplan-remote-timeout", 5);
  799. $hash->{loglevel} = AttrVal($name, "dbplan-remote-loglevel", 4);
  800. $hash->{buf} = AttrVal($name, "dbplan-remote-loglevel", 1);
  801. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): next getting $hash->{url}";
  802. HttpUtils_NonblockingGet($hash);
  803. return;
  804. }
  805. # $data = `cat /opt/fhem/FHEM/test.html`; # note backticks, qw"cat ..." also works
  806. #use HTML::Tree;
  807. #my $tree = HTML::Tree->new();
  808. #$tree->parse($data);
  809. #my @inhalt = $tree->look_down('class', qr/rline.*/);
  810. #Log3 $name, 3, "DBPlan ($name) - DBPlan_Tree: vorher";
  811. #foreach my $thumb (@inhalt) {
  812. # my $ausgabe = $thumb->as_text;
  813. # Log3 $name, 3, "DBPlan ($name) - DBPlan_Tree: $ausgabe\r\n";
  814. #}
  815. #Log3 $name, 3, "DBPlan ($name) - DBPlan_Tree: nach";
  816. # my $ausgabe = $tree->as_text;
  817. # Log3 $name, 3, "DBPlan ($name) - DBPlan_Tree: $ausgabe\r\n";
  818. my $pattern;
  819. # "dbplan-special-char-decode:none,utf8,latin1(default) "
  820. my $convChar = AttrVal($name, "dbplan-special-char-decode", "latin1(default)");
  821. my $defChar = AttrVal($name, "dbplan-default-char", "none");
  822. my @readings_list = split("(,|\\|)", AttrVal($name, "dbplan-reading-deselect", "none"));
  823. readingsBeginUpdate($hash);
  824. Log3 $name, 5, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): Callback called: Hash: $hash, data: $data\r\n";
  825. ##################################################################################
  826. # only for debugging
  827. if(AttrVal($name, "verbose", 3) >= 5) {
  828. readingsBulkUpdate( $hash, "dbg_travel_notes_HTML_1", $data );
  829. }
  830. ##################################################################################
  831. # Parsing error information
  832. if(!(grep { /^(travel_note_error)$/ } @readings_list)) {
  833. $pattern = '\<div.class="errormsg"\>..(.*?)\<\/div\>';
  834. if ($data =~ m/$pattern/s) {
  835. my $notification = $1;
  836. if($convChar eq "latin1(default)"){
  837. $notification = DBPlan_html2uml($notification);
  838. }
  839. if($convChar eq "utf8"){
  840. $notification = DBPlan_decode(DBPlan_html2uml($notification));
  841. }
  842. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): travel error information for plan $index read successfully";
  843. readingsBulkUpdate( $hash, "travel_note_error_$index", $notification);
  844. } else {
  845. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): no error information for plan $index found";
  846. readingsBulkUpdate( $hash, "travel_note_error_$index", $defChar) if($defChar ne "delete");
  847. }
  848. }
  849. ##################################################################################
  850. # Parsing notification
  851. if(!(grep { /^(travel_note_text)$/ } @readings_list)) {
  852. $pattern = '\<\/script\>.\<div.class="red.bold.haupt".\>(.*?)\<br.\/\>.\<\/div\>';
  853. my $pattern2 = '(Fahrt f&#228;llt aus)';
  854. my $pattern3 = '(Aktuelle Informationen zu der Verbindung)';
  855. if ($data =~ m/$pattern/s) {
  856. my $notification = $1;
  857. if($convChar eq "latin1(default)"){
  858. $notification = DBPlan_html2uml($notification);
  859. }
  860. if($convChar eq "utf8"){
  861. $notification = DBPlan_decode(DBPlan_html2uml($notification));
  862. }
  863. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): travel notification for plan $index read successfully";
  864. readingsBulkUpdate( $hash, "travel_note_text_$index", $notification);
  865. } elsif ($data =~ m/$pattern2/s) {
  866. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): travel notification for plan $index read successfully";
  867. readingsBulkUpdate( $hash, "travel_note_text_$index", "Fahrt faellt aus");
  868. } elsif ($data =~ m/$pattern3/s) {
  869. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): travel notification for plan $index read successfully";
  870. readingsBulkUpdate( $hash, "travel_note_text_$index", "Aktuelle Informationen liegen vor");
  871. } else {
  872. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): no actual informations for plan $index found";
  873. readingsBulkUpdate( $hash, "travel_note_text_$index", $defChar) if($defChar ne "delete");
  874. }
  875. }
  876. ##################################################################################
  877. # Parsing notification
  878. #$pattern = 'alt="".\/\>Angebot.w\&\#228\;hlen.*?\>(.*?)\<\/div\>.\<div.class="querysummary1.clickarea"';
  879. #if ($data =~ m/$pattern/s) {
  880. # Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): travel notification for plan $index read successfully";
  881. # readingsBulkUpdate( $hash, "travel_note_text_$index", DBPlan_html2txt($1));
  882. #} else {
  883. # Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): no travel notes for plan $index found";
  884. #}
  885. ##################################################################################
  886. # Parsing notification
  887. #$pattern = 'alt="".\/\>Angebot.w\&\#228\;hlen(.*?)\<\/div\>.\<div class="clickarea';
  888. #if ($data =~ m/$pattern/s) {
  889. # Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): travel notification for plan $index read successfully";
  890. # readingsBulkUpdate( $hash, "travel_note_text_$index", DBPlan_html2txt($1));
  891. #} else {
  892. # Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): no travel notes for plan $index found";
  893. #}
  894. my $plattform = "";
  895. ##################################################################################
  896. # Parsing departure plattform
  897. if(!(grep { /^(travel_departure)$/ } @readings_list)) {
  898. $pattern = '\<\/span\>.(Gl.*?).\<br.\/\>.\<\/div\>.\<div.class="rline.haupt.mot"\>';
  899. $plattform = 'none';
  900. if ($data =~ m/$pattern/s) {
  901. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): travel departure plattform for plan $index read successfully";
  902. $plattform = $1;
  903. if($convChar eq "latin1(default)"){
  904. $plattform = DBPlan_html2uml($plattform);
  905. }
  906. if($convChar eq "utf8"){
  907. $plattform = DBPlan_decode(DBPlan_html2uml($plattform));
  908. }
  909. } else {
  910. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): no travel departure plattform for plan $index found";
  911. }
  912. # and then parsing departure place
  913. $pattern = '"rline.haupt.routeStart".style="."\>.\<span.class="bold"\>(.*?)\<\/span\>';
  914. if ($data =~ m/$pattern/s) {
  915. $plattform = $1.' - '.$plattform;
  916. if($convChar eq "latin1(default)"){
  917. $plattform = DBPlan_html2uml($plattform);
  918. }
  919. if($convChar eq "utf8"){
  920. $plattform = DBPlan_decode(DBPlan_html2uml($plattform));
  921. }
  922. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): travel departure for plan $index read successfully";
  923. readingsBulkUpdate( $hash, "travel_departure_$index", $plattform);
  924. }
  925. if( $plattform eq "none") {
  926. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): no travel departure for plan $index found";
  927. readingsBulkUpdate( $hash, "travel_departure_$index", $defChar) if($defChar ne "delete");
  928. }
  929. }
  930. ##################################################################################
  931. # vehicle number(s)
  932. if(!(grep { /^(travel_vehicle_nr)$/ } @readings_list)) {
  933. $pattern = '\<div.class="rline.haupt.mot"\>.\<div.class="motSection"\>.*?\<span.class="bold"\>.(.*?).\<\/span\>.\<\/a\>.\<\/div\>.\<\/div\>.\<div.class="rline.haupt.route.*?"\>';
  934. $plattform = 'none';
  935. if ($data =~ m/$pattern/s) {
  936. $plattform = '';
  937. foreach my $vehicle ($data =~ m/$pattern/gs) {
  938. $vehicle =~ s/\r|\n//g;
  939. $vehicle =~ s/&nbsp.*//gs;
  940. $vehicle =~ s/\<span.class="red.*//gs;
  941. $vehicle =~ s/\s+/ /g;
  942. $plattform .= $vehicle . " | ";
  943. }
  944. $plattform = substr($plattform, 0, -3);
  945. readingsBulkUpdate( $hash, "travel_vehicle_nr_$index", $plattform);
  946. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): vehicle numbers for plan $index read successfully";
  947. } else {
  948. readingsBulkUpdate( $hash, "travel_vehicle_nr_$index", $defChar) if($defChar ne "delete");
  949. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): no vehicle numbers for plan $index found";
  950. }
  951. }
  952. ##################################################################################
  953. # Parsing destination plattform
  954. if(!(grep { /^(travel_destination)$/ } @readings_list)) {
  955. $pattern = '\<div.class="rline.haupt.routeEnd.routeEnd__IV"\>.*?(Gl.*?).\<br.\/\>.\<span.class="bold"\>.*?\<\/span\>';
  956. $plattform = 'none';
  957. if ($data =~ m/$pattern/s) {
  958. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): travel destination plattform for plan $index read successfully";
  959. $plattform = $1;
  960. if($convChar eq "latin1(default)"){
  961. $plattform = DBPlan_html2uml($plattform);
  962. }
  963. if($convChar eq "utf8"){
  964. $plattform = DBPlan_decode(DBPlan_html2uml($plattform));
  965. }
  966. readingsBulkUpdate( $hash, "travel_destination_$index", $plattform);
  967. } else {
  968. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): no travel destination plattform for plan $index found";
  969. }
  970. # and then parsing destination place
  971. $pattern = '\<div.class="rline.haupt.routeEnd.routeEnd__IV"\>.*?\<br.\/\>.\<span.class="bold"\>(.*?)\<\/span\>';
  972. if ($data =~ m/$pattern/s) {
  973. $plattform = $1.' - '.$plattform;
  974. if($convChar eq "latin1(default)"){
  975. $plattform = DBPlan_html2uml($plattform);
  976. }
  977. if($convChar eq "utf8"){
  978. $plattform = DBPlan_decode(DBPlan_html2uml($plattform));
  979. }
  980. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): travel destination for plan $index read successfully";
  981. readingsBulkUpdate( $hash, "travel_destination_$index", $plattform);
  982. }
  983. if ($plattform eq 'none') {
  984. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): no travel destination for plan $index found";
  985. readingsBulkUpdate( $hash, "travel_destination_$index", $defChar) if($defChar ne "delete");
  986. }
  987. }
  988. ##################################################################################
  989. # delays
  990. Log3 $name, 5, "DBPlan ($name) - departure $index: " . $d_dly;
  991. Log3 $name, 5, "DBPlan ($name) - arrival $index: " . $a_dly;
  992. if (!$d_dly) {
  993. $pattern = '\<\/span\>.\<span.class="querysummary2".id="dtlOpen_2"\>.*?.\<span.class="delay"\>(\d\d:\d\d)\<\/span\>.-.*?\<\/div\>.\<div.class="rline.haupt.routeStart".style="."\>';
  994. if (($data =~ m/$pattern/s) && ($hash->{READINGS}{"plan_departure_delay_$index"}{VAL} eq "0")) {
  995. my $dTime = $hash->{READINGS}{"plan_departure_$index"}{VAL};
  996. Log3 $name, 4, "DBPlan ($name) - DBPlan_DATA_Delays_1: $dTime to $1";
  997. readingsBulkUpdate( $hash, "plan_departure_delay_$index", ($dTime =~ m|(\d\d):(\d\d)|)?DBPlan_getMinutesDiff($dTime, $1):"0");
  998. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): departure delay for plan $index read successfully: $index $dTime, $1";
  999. } else {
  1000. readingsBulkUpdate( $hash, "plan_departure_delay_$index", "0") if($defChar ne "delete");
  1001. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): no departure delay for plan $index found";
  1002. }
  1003. }
  1004. if (!$a_dly) {
  1005. # $pattern = '\<\/span\>.\<span.class="querysummary2".id="dtlOpen_2"\>.*?.\<span.class=".*?"\>(.*?)\<\/span\>.*?\<span.class=".*?"\>(.*?)\<\/span\>.\<\/span\>.\<\/a\>.\<\/div\>.\<div.class="rline.haupt.routeStart".style="."\>';
  1006. $pattern = '\<\/span\>.\<span.class="querysummary2".id="dtlOpen_2"\>.*?.-.*?<span.class="delay"\>(\d\d:\d\d)\<\/span\>.\<\/span\>.\<\/a\>.\<\/div\>.*?\<div.class="rline.haupt.routeStart".style="."\>';
  1007. # Log3 $name, 2, "DBPlan ($name) - DBPlan_DATA_Delays: $data";
  1008. if (($data =~ m/$pattern/s) && ($hash->{READINGS}{"plan_arrival_delay_$index"}{VAL} eq "0")) {
  1009. my $dTime = $hash->{READINGS}{"plan_arrival_$index"}{VAL};
  1010. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes: $dTime to $1";
  1011. readingsBulkUpdate( $hash, "plan_arrival_delay_$index", ($dTime =~ m|(\d\d):(\d\d)|)?DBPlan_getMinutesDiff($dTime, $1):"0");
  1012. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): arrival delay for plan $index read successfully: $index $dTime, $1";
  1013. } else {
  1014. readingsBulkUpdate( $hash, "plan_arrival_delay_$index", "0") if($defChar ne "delete");
  1015. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): no arrival delay for plan $index found";
  1016. }
  1017. }
  1018. readingsEndUpdate($hash, 1);
  1019. ##################################################################################
  1020. # Recursiv call until index = 0
  1021. $index -= 1;
  1022. my $note_text = "travel_note_link_$index";
  1023. if($index <= 0) {
  1024. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): parsed all notes";
  1025. return;
  1026. }
  1027. $hash->{helper}{note_index} = $index;
  1028. $hash->{url} = $hash->{helper}{$note_text};
  1029. $hash->{callback} = \&DBPlan_Parse_Travel_Notes;
  1030. $hash->{noshutdown} = AttrVal($name, "dbplan-remote-noshutdown", 1);
  1031. $hash->{timeout} = AttrVal($name, "dbplan-remote-timeout", 5);
  1032. $hash->{loglevel} = AttrVal($name, "dbplan-remote-loglevel", 4);
  1033. $hash->{buf} = AttrVal($name, "dbplan-remote-loglevel", 1);
  1034. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Travel_Notes ($index): next getting $hash->{url}";
  1035. HttpUtils_NonblockingGet($hash);
  1036. return;
  1037. }
  1038. #####################################
  1039. # Parsing the DB main timetable
  1040. #
  1041. sub DBPlan_Parse_Timetable($)
  1042. {
  1043. my ($hash, $err, $data) = @_;
  1044. my $name = $hash->{NAME};
  1045. delete($hash->{error}) if(exists($hash->{error}));
  1046. if ($err) {
  1047. Log3 $name, 3, "DBPlan ($name) - DBPlan_Parse_Timetable: got error in callback: $err";
  1048. return undef;
  1049. }
  1050. if($data eq "")
  1051. {
  1052. Log3 $name, 3, "DBPlan ($name) - DBPlan_Parse_Timetable: received http without any data after requesting DB timetable";
  1053. return undef;
  1054. }
  1055. Log3 $name, 5, "DBPlan ($name) - DBPlan_Parse_Timetable: Callback called with Hash: $hash, data: $data\r\n";
  1056. if(AttrVal($name, "verbose", 3) >= 5) {
  1057. $hash->{Timetable} = $data;
  1058. }
  1059. ##################################################################################
  1060. # only for debugging
  1061. if(AttrVal($name, "verbose", 3) >= 5) {
  1062. readingsBeginUpdate($hash);
  1063. readingsBulkUpdate( $hash, "dbg_db_plan", $data );
  1064. readingsEndUpdate( $hash, 1 );
  1065. }
  1066. my $i;
  1067. my $ret;
  1068. my $defChar = AttrVal($name, "dbplan-default-char", "none");
  1069. $ret = fhem("deletereading $name dbg.*", 1);
  1070. if($defChar eq "delete") {
  1071. $ret = fhem("deletereading $name plan.*", 1);
  1072. $ret = fhem("deletereading $name travel.*", 1);
  1073. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Timetable: readings deleted";
  1074. } else {
  1075. $defChar =" " if($defChar eq "nochar");
  1076. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Timetable: readings filled with: $defChar";
  1077. }
  1078. my $pattern = '\<div class="haupt bline leftnarrow"\>(.*?)\<div class="bline bggrey stdpadding"\>';
  1079. if ($data =~ m/MOBI_ASK_DEU_de_error/s) {
  1080. Log3 $name, 3, "DBPlan ($name) - error in DB request. Bitte Log prüfen.";
  1081. readingsBeginUpdate($hash);
  1082. readingsBulkUpdate( $hash, "plan_error", "error in DB request" );
  1083. readingsEndUpdate( $hash, 1 );
  1084. if ($data =~ m/$pattern/s) {
  1085. my $error_text = DBPlan_html2txt($1);
  1086. Log3 $name, 3, "DBPlan ($name) - DBPlan_Parse_Timetable: error description of DB timetable request: $error_text";
  1087. }
  1088. return undef;
  1089. }
  1090. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Timetable: successfully identified";
  1091. ##################################################################################
  1092. # Testing correct answer. DB timetable will show three connection plans
  1093. $pattern = 'verbindung.start.=.new.Object\(\);(.*?)digitalData.verbindung.push\(verbindung\)'
  1094. .'.*?'
  1095. .'verbindung.start.=.new.Object\(\);(.*?)digitalData.verbindung.push\(verbindung\)'
  1096. .'.*?'
  1097. .'verbindung.start.=.new.Object\(\);(.*?)digitalData.verbindung.push\(verbindung\)';
  1098. if ($data =~ m/$pattern/s) {
  1099. ##################################################################################
  1100. # only for debugging
  1101. if(AttrVal($name, "verbose", 3) >= 5) {
  1102. readingsBeginUpdate($hash);
  1103. readingsBulkUpdate( $hash, "dbg_connect_plan_1", (defined($1))?$1:$defChar );
  1104. readingsBulkUpdate( $hash, "dbg_connect_plan_2", (defined($2))?$3:$defChar );
  1105. readingsBulkUpdate( $hash, "dbg_connect_plan_3", (defined($3))?$2:$defChar );
  1106. readingsEndUpdate( $hash, 1 );
  1107. }
  1108. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Timetable: connection plans read successfully";
  1109. if($hash->{DevState} eq 'initialized' || $hash->{DevState} eq 'inactive') {
  1110. $hash->{DevState}='active';
  1111. #$hash->{state}='active';
  1112. readingsSingleUpdate($hash, "state", "active", 1);
  1113. }
  1114. } else {
  1115. Log3 $name, 3, "DBPlan ($name) - DBPlan_Parse_Timetable: no connection plans found";
  1116. return undef;
  1117. }
  1118. ##################################################################################
  1119. # Extracting connection plan with TableExtract (Three is default)
  1120. my @plan;
  1121. my @planrow;
  1122. my $notelink = '';
  1123. $plan[1] = $1;
  1124. $plan[2] = $2;
  1125. $plan[3] = $3;
  1126. my $rc = eval {
  1127. require HTML::TableExtract;
  1128. HTML::TableExtract->import();
  1129. 1;
  1130. };
  1131. unless($rc)
  1132. {
  1133. readingsBeginUpdate($hash);
  1134. readingsBulkUpdate( $hash, "plan_error", "Error loading HTML::TableExtract" );
  1135. readingsEndUpdate( $hash, 1 );
  1136. Log3 $name, 2, "DBPlan ($name) - Timetable: Error loading HTML::TableExtract. May be this module is not installed.";
  1137. return $rc;
  1138. }
  1139. $data =~ s/\<td.class="ovHeadNoPad"\>&nbsp;\<\/td\>/\<td class="ovHead"\>\nLeer\<br \/\>\nLeer\n\<\/td\>/g;
  1140. $data =~ s/&nbsp;/ /g;
  1141. # Log3 $name, 2, $data;
  1142. my @headers = split(/ /, AttrVal($name, "dbplan-table-headers", "An Leer Dauer Preis"));
  1143. Log3 $name, 4, "DBPlan ($name) - Timetable-Headers: @headers";
  1144. my $timetable = HTML::TableExtract->new( headers => \@headers );
  1145. my $retRow = "";
  1146. Log3 $name, 4, "DBPlan ($name) - Timetable: data for HTML::TableExtract: \n $data";
  1147. $ret = $timetable->parse($data);
  1148. $i = 0;
  1149. my $filler = "";
  1150. foreach my $ts ($timetable->tables) {
  1151. Log3 $name, 5, "DBPlan ($name) - Timetable: Erste Schleife";
  1152. foreach my $row ($timetable->rows) {
  1153. Log3 $name, 5, "DBPlan ($name) - Timetable: Zweite Schleife";
  1154. Log3 $name, 4, "DBPlan ($name) - Timetable-Org1: $retRow";
  1155. if(@$row) {
  1156. my @myValues = map defined($_) ? $_ : '', @$row;
  1157. $retRow = join(';', @myValues);
  1158. $retRow =~ s/\n|\r/;/g; #s,[\r\n]*,,g;
  1159. if($defChar ne "delete") {
  1160. $retRow =~ s/ /$defChar/g;
  1161. } else {
  1162. $retRow =~ s/ /$filler/g;
  1163. }
  1164. }
  1165. Log3 $name, 4, "DBPlan ($name) - Timetable-Org2: $retRow";
  1166. $planrow[$i++] = $retRow;
  1167. }
  1168. }
  1169. Log3 $name, 4, "DBPlan ($name) - Read Timetablelines: $i";
  1170. unless(@planrow) {
  1171. Log3 $name, 2, "DBPlan ($name) - Timetable: HTML::TableExtract failed.";
  1172. readingsBeginUpdate($hash);
  1173. readingsBulkUpdate( $hash, "plan_error", "Error HTML::TableExtract failed" );
  1174. readingsEndUpdate( $hash, 1 );
  1175. return undef;
  1176. }
  1177. my @readings_list = split("(,|\\|)", AttrVal($name, "dbplan-reading-deselect", "none"));
  1178. my $phrase = "";
  1179. for($i=0;$i<@readings_list;$i++) {
  1180. $phrase = $readings_list[$i] . "_\\d";
  1181. $ret = fhem("deletereading $name $phrase", 1);
  1182. Log3 $name, 4, "DBPlan ($name) - deleteing reading: $phrase";
  1183. }
  1184. my $ai = 0;
  1185. my $note_text = "";
  1186. my @d_delay = (0, 0, 0);
  1187. my @a_delay = (0, 0, 0);
  1188. readingsBeginUpdate($hash);
  1189. for($i=1; $i<=3; $i++) {
  1190. Log3 $name, 4, "DBPlan ($name) - Read Timetableposition: $ai for counter:$i";
  1191. Log3 $name, 4, "DBPlan ($name) - Show Timetableposition [$ai]:$planrow[$ai]";
  1192. my ($d_time, $a_time, $d_delay, $a_delay, $change, $duration, $prod, $price) = split(";", $planrow[$ai]);
  1193. if(! ($d_time =~ m|(\d\d):(\d\d)|) ) {
  1194. $ai++;
  1195. Log3 $name, 4, "DBPlan ($name) - TimetableError: $i -> $ai while: $d_time";
  1196. ($d_time, $a_time, $d_delay, $a_delay, $change, $duration, $prod, $price) = split(";", $planrow[$ai]);
  1197. Log3 $name, 4, "DBPlan ($name) - Show Timetableposition [$ai]:$planrow[$ai]";
  1198. }
  1199. $ai++;
  1200. $change = "" unless(defined($change));
  1201. $duration = "" unless(defined($duration));
  1202. $prod = "" unless(defined($prod));
  1203. $price = "" unless(defined($price));
  1204. Log3 $name, 4, "DBPlan ($name) - Timetable $i/$ai: $d_time - $a_time - $d_delay - $a_delay - $change - $duration - $prod - $price";
  1205. if(!(grep { /^(plan_departure)$/ } @readings_list)) {
  1206. readingsBulkUpdate( $hash, "plan_departure_$i", ($d_time =~ m|(\d\d):(\d\d)|)?$d_time:$defChar );
  1207. }
  1208. if(!(grep { /^(plan_arrival)$/ } @readings_list)) {
  1209. readingsBulkUpdate( $hash, "plan_arrival_$i", ($a_time =~ m|(\d\d):(\d\d)|)?$a_time:$defChar );
  1210. }
  1211. if(!(grep { /^(plan_connection)$/ } @readings_list)) {
  1212. readingsBulkUpdate( $hash, "plan_connection_$i", (trim($prod) ne "")?$prod:$defChar );
  1213. }
  1214. if(!(grep { /^(plan_departure_delay)$/ } @readings_list)) {
  1215. if(($d_time =~ m|(\d\d):(\d\d)|) && ($d_delay =~ m|(\d\d):(\d\d)|)) {
  1216. my $delay = DBPlan_getMinutesDiff($d_time, $d_delay);
  1217. readingsBulkUpdate( $hash, "plan_departure_delay_$i", $delay );
  1218. $d_delay[$i - 1] = 1;
  1219. Log3 $name, 4, "DBPlan ($name) - departure delay: $delay";
  1220. } else {
  1221. readingsBulkUpdate( $hash, "plan_departure_delay_$i", "0" ) if($defChar ne "delete");
  1222. }
  1223. }
  1224. if(!(grep { /^(plan_arrival_delay)$/ } @readings_list)) {
  1225. if(($a_time =~ m|(\d\d):(\d\d)|) && ($a_delay =~ m|(\d\d):(\d\d)|)) {
  1226. my $delay = DBPlan_getMinutesDiff($a_time, $a_delay);
  1227. readingsBulkUpdate( $hash, "plan_arrival_delay_$i", $delay );
  1228. $a_delay[$i - 1] = 1;
  1229. Log3 $name, 4, "DBPlan ($name) - arrival delay: $delay";
  1230. } else {
  1231. readingsBulkUpdate( $hash, "plan_arrival_delay_$i", "0" ) if($defChar ne "delete");
  1232. }
  1233. } else {
  1234. $a_delay[$i - 1] = 1;
  1235. }
  1236. if(!(grep { /^(plan_travel_duration)$/ } @readings_list)) {
  1237. readingsBulkUpdate( $hash, "plan_travel_duration_$i", (trim($duration) ne "")?$duration:$defChar );
  1238. }
  1239. if(!(grep { /^(plan_travel_change)$/ } @readings_list)) {
  1240. readingsBulkUpdate( $hash, "plan_travel_change_$i", (trim($change) ne "")?$change:$defChar );
  1241. }
  1242. if(!(grep { /^(travel_price)$/ } @readings_list)) {
  1243. readingsBulkUpdate( $hash, "travel_price_$i", (trim($price) ne "")?$price:$defChar);
  1244. }
  1245. ##################################################################################
  1246. # Parsing travel notes (notifications)
  1247. # http://www.img-bahn.de/v/1504/img/achtung_17x19_mitschatten.png
  1248. if(!(grep { /^(travel_note)$/ } @readings_list)) {
  1249. $pattern = '\<img src=".*?img\/(.*?)_.*?"\ \/\>\<\/a\>';
  1250. if ($plan[$i] =~ m/$pattern/s) {
  1251. readingsBulkUpdate( $hash, "travel_note_$i", (trim($1) ne "")?$1:$defChar);
  1252. # readingsBulkUpdate( $hash, "plan_departure_delay_$i", "Hinweise" ) if(trim($1) ne "");
  1253. # readingsBulkUpdate( $hash, "plan_arrival_delay_$i", "Hinweise" ) if(trim($1) ne "");
  1254. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Timetable: travel note for plan $i read successfully";
  1255. } else {
  1256. readingsBulkUpdate( $hash, "travel_note_$i", $defChar) if($defChar ne "delete");
  1257. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Timetable: no travel note for plan $i found";
  1258. }
  1259. }
  1260. ##################################################################################
  1261. # Parsing URL for further informations about travel notes (notifications)
  1262. $note_text = "travel_note_link_$i";
  1263. $pattern = '\<a href="(.*?)amp\;"\>';
  1264. if ($plan[$i] =~ m/$pattern/s) {
  1265. $notelink = $1;
  1266. $notelink =~ s/amp\;//g;
  1267. $notelink =~ s/details=opened.*?yes&/detailsVerbund=opened!/g;
  1268. $hash->{helper}{$note_text} = $notelink;
  1269. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Timetable: travel note URL for plan $i: $notelink";
  1270. # only for debugging
  1271. if(AttrVal($name, "verbose", 3) >= 4) {
  1272. readingsBulkUpdate( $hash, "dbg_travel_note_link_$i", $notelink);
  1273. }
  1274. } else {
  1275. $hash->{helper}{$note_text} = "no URL found";
  1276. Log3 $name, 4, "DBPlan ($name) - DBPlan_Parse_Timetable: no travel note URL for plan $i found";
  1277. # only for debugging
  1278. if(AttrVal($name, "verbose", 3) >= 4) {
  1279. readingsBulkUpdate( $hash, "dbg_travel_note_link_$i", "no URL found");
  1280. }
  1281. }
  1282. } #end for
  1283. readingsEndUpdate( $hash, 1 );
  1284. ##################################################################################
  1285. # First call of recursiv call of URL for further informations about travel notes (notifications)
  1286. $i--;
  1287. $note_text = "travel_note_link_$i";
  1288. #$hash->{url} = ReadingsVal($name, "travel_note_link_$hash->{note_index}", "");
  1289. $hash->{helper}{note_index} = $i;
  1290. $hash->{url} = $hash->{helper}{$note_text};
  1291. $hash->{callback} = \&DBPlan_Parse_Travel_Notes;
  1292. $hash->{noshutdown} = AttrVal($name, "dbplan-remote-noshutdown", 1);
  1293. $hash->{timeout} = AttrVal($name, "dbplan-remote-timeout", 5);
  1294. $hash->{loglevel} = AttrVal($name, "dbplan-remote-loglevel", 4);
  1295. $hash->{buf} = AttrVal($name, "dbplan-remote-loglevel", 1);
  1296. $hash->{helper}{delay_1} = $d_delay[0] . "," . $a_delay[0];
  1297. $hash->{helper}{delay_2} = $d_delay[1] . "," . $a_delay[1];
  1298. $hash->{helper}{delay_3} = $d_delay[2] . "," . $a_delay[2];
  1299. Log3 $name, 4, "DBPlan ($name) - DB notes ($hash->{helper}{note_index}): next getting $hash->{url}";
  1300. HttpUtils_NonblockingGet($hash);
  1301. return undef;
  1302. }
  1303. #####################################
  1304. # replaces all HTML entities to their utf-8 counter parts.
  1305. # c3 bc = ü
  1306. # c3 9f = ß
  1307. # c3 b6 = ö
  1308. # c3 a4 = ä
  1309. # c3 84 = Ä
  1310. # c3 96 = Ö
  1311. # c3 9c = Ü
  1312. # c2 ab = "
  1313. # c2 bb = "
  1314. # e2 80 = ""
  1315. # c2 ad = -
  1316. #
  1317. sub DBPlan_html2txt($)
  1318. {
  1319. my ($string) = @_;
  1320. $string =~ s/&nbsp;/ /g;
  1321. $string =~ s/&amp;/&/g;
  1322. $string =~ s/(\xe4|\xc3\xa4|&auml;|\\u00e4|\\u00E4|&#228;)/ä/g;
  1323. $string =~ s/(\xc4|\xc3\x84|&Auml;|\\u00c4|\\u00C4|&#196;)/Ä/g;
  1324. $string =~ s/(\xf6|\xc3\xb6|&ouml;|\\u00f6|\\u00F6|&#246;)/ö/g;
  1325. $string =~ s/(\xd6|\xc3\x96|&Ouml;|\\u00d6|\\u00D6|&#214;)/Ö/g;
  1326. $string =~ s/(\xfc|\xc3\xbc|&uuml;|\\u00fc|\\u00FC|&#252;)/ü/g;
  1327. $string =~ s/(\xdc|\xc3\x9c|&Uuml;|\\u00dc|\\u00DC|&#220;)/Ü/g;
  1328. $string =~ s/(\xdf|\xc3\x9f|&szlig;|&#223;)/ß/g;
  1329. $string =~ s/<.+?>//g;
  1330. $string =~ s/(^\s+|\s+$)//g;
  1331. return trim($string);
  1332. }
  1333. sub DBPlan_html2uml($)
  1334. {
  1335. my ($string) = @_;
  1336. $string =~ s/&nbsp;/ /g;
  1337. $string =~ s/&amp;/&/g;
  1338. $string =~ s/(\xe4|\xc3\xa4|&auml;|\\u00e4|\\u00E4|&#228;)/ä/g;
  1339. $string =~ s/(\xc4|\xc3\x84|&Auml;|\\u00c4|\\u00C4|&#196;)/Ä/g;
  1340. $string =~ s/(\xf6|\xc3\xb6|&ouml;|\\u00f6|\\u00F6|&#246;)/ö/g;
  1341. $string =~ s/(\xd6|\xc3\x96|&Ouml;|\\u00d6|\\u00D6|&#214;)/Ö/g;
  1342. $string =~ s/(\xfc|\xc3\xbc|&uuml;|\\u00fc|\\u00FC|&#252;)/ü/g;
  1343. $string =~ s/(\xdc|\xc3\x9c|&Uuml;|\\u00dc|\\u00DC|&#220;)/Ü/g;
  1344. $string =~ s/(\xdf|\xc3\x9f|&szlig;|&#223;)/ß/g;
  1345. $string =~ s/<.+?>//g;
  1346. $string =~ s/(^\s+|\s+$)//g;
  1347. return trim($string);
  1348. }
  1349. # UTF8
  1350. sub DBPlan_decode($) {
  1351. my($text) = @_;
  1352. $text =~ s/ä/ä/g;
  1353. $text =~ s/Ä/Ä/g;
  1354. $text =~ s/ö/ö/g;
  1355. $text =~ s/Ö/Ã/g;
  1356. $text =~ s/ü/ü/g;
  1357. $text =~ s/Ü/Ü/g;
  1358. $text =~ s/ß/ß/g;
  1359. $text =~ s/´/´/g;
  1360. $text =~ s/"/„/g;
  1361. return $text;
  1362. }
  1363. #####################################
  1364. # loads the stations from file
  1365. sub DBPlan_loadStationFile($;$)
  1366. {
  1367. my ($hash, $file) = @_;
  1368. my @stationfile;
  1369. my @tmpline;
  1370. my $name = $hash->{NAME};
  1371. my $err;
  1372. $file = AttrVal($hash->{NAME}, "dbplan-station-file", "") unless(defined($file));
  1373. if($file ne "" and -r $file)
  1374. {
  1375. delete($hash->{helper}{STATIONFILE}) if(defined($hash->{helper}{STATIONFILE}));
  1376. delete($hash->{helper}{STATION_NAMES}) if(defined($hash->{helper}{STATION_NAMES}));
  1377. Log3 $hash->{NAME}, 3, "DBPlan ($name) - loading station file $file";
  1378. ($err, @stationfile) = FileRead($file);
  1379. unless(defined($err) and $err)
  1380. {
  1381. foreach my $line (@stationfile)
  1382. {
  1383. if(not $line =~ /^\s*$/)
  1384. {
  1385. chomp $line;
  1386. $line =~ s/\n|\r//;
  1387. Log3 $name, 5, "DBPlan ($name) - $line " . $tmpline[6];
  1388. @tmpline = split(";", $line);
  1389. if(@tmpline >= 2)
  1390. {
  1391. $hash->{helper}{STATION_NAMES}{$tmpline[0]} = $tmpline[6];
  1392. }
  1393. }
  1394. }
  1395. my $count_stations = scalar keys %{$hash->{helper}{STATION_NAMES}};
  1396. Log3 $name, 2, "DBPlan ($name) - read ".($count_stations > 0 ? $count_stations : "no")." station".($count_stations == 1 ? "" : "s")." from station file";
  1397. }
  1398. else
  1399. {
  1400. Log3 $name, 3, "DBplan ($name) - could not open station file: $err";
  1401. }
  1402. }
  1403. else
  1404. {
  1405. Log3 $name, 3, "DBPlan ($name) - unable to access station file: $file";
  1406. return ("unable to access station file: $file");
  1407. }
  1408. }
  1409. #####################################
  1410. # only for testing regular expressions
  1411. sub RegExTest()
  1412. {
  1413. # my $test = '<h1>Fehler</h1> <div class="haupt bline"> Sehr geehrte Kundin, sehr geehrter Kunde,<br /><br /> Start/Ziel/Via oder &#228;quivalente Bahnh&#246;fe sind mehrfach vorhanden oder identisch. <br />Wir bitten Sie, Ihre Anfrage mit ge&#228;nderten Eingaben zu wiederholen. <br /><span class="bold"><br />Vielen Dank! <br />Ihr Team von www.bahn.de</span><br /> <br />Code: H9380<br /> </div> <div class="bline"> <a href="https://reiseauskunft.bahn.de/bin/detect.exe/dox?" ';
  1414. my $test = ReadingsVal("DB_Test", "Notes_HTML_1", "none");
  1415. my $pattern = '';
  1416. $pattern = '"rline.haupt.stationDark.routeStart".style="."\>.\<span.class="bold"\>(.*?)\<\/span\>';
  1417. if ($test =~ m/$pattern/s) {
  1418. my $error_text = DBPlan_html2txt($1);
  1419. return ("$1 \n\n$2 \n\n$3");
  1420. }
  1421. return ("Kein Ergebnis: $test");
  1422. }
  1423. 1;
  1424. =pod
  1425. =begin html
  1426. <a name="DBPlan"></a>
  1427. <h3>DBPlan</h3>
  1428. <ul>
  1429. The module fetches from the info page of the DB <https://reiseauskunft.bin.de/bin/query.exe/dox?S=departure&Z=destination&start=1&rt=1>
  1430. up-to-date information on a specified connection and stores it in Fhem readings.
  1431. The file with the IBNR codes and stations of Deutsche Bahn can be download at http://www.michaeldittrich.de/ibnr.
  1432. <br><br>
  1433. <b>Prerequisites</b>
  1434. <ul>
  1435. <br>
  1436. <li>
  1437. This Module uses the non blocking HTTP function HttpUtils_NonblockingGet provided by FHEM's HttpUtils in a new Version published in December 2013.<br>
  1438. If the module is not already present in your Fhem environment, please update FHEM via the update command.<br>
  1439. </li>
  1440. </ul>
  1441. <br>
  1442. State will show the device status (DevState):
  1443. <ul>
  1444. <li><b>initialized</b></li>
  1445. the device is defined, but no successfully requests and parsing has been done<br>
  1446. this state will also be set when changing from <inactive> to <active> and <disabled> to <enabled><br>
  1447. <li><b>active</b></li>
  1448. the device is working<br>
  1449. <li><b>stopped</b></li>
  1450. the device timer has been stopped. A reread is possibel<br>
  1451. <li><b>disabled</b></li>
  1452. the device is disabled.<br>
  1453. </ul>
  1454. <br>
  1455. <a name="DBPlandefine"></a>
  1456. <b>Define</b>
  1457. <ul>
  1458. <br>
  1459. <code>define &lt;name&gt; DBPlan &lt;Refresh interval in seconds [time offset in minutes]&gt;</code>
  1460. <br><br>
  1461. The module connects to the given URL every Interval seconds and then parses the response. If time_offset is
  1462. defined, the moudules uses the actual time + time_offset as start point<br>
  1463. <br>
  1464. Example:<br>
  1465. <br>
  1466. <ul><code>define DBPlan_Test DBPlan 60</code></ul>
  1467. </ul>
  1468. <br>
  1469. <a name="DBPlanconfiguration"></a>
  1470. <b>Configuration of DBPlan</b><br><br>
  1471. <ul>
  1472. Example for a timetable query:<br><br>
  1473. <ul><code>
  1474. attr DB_Test dbplan_station Köln-Weiden West
  1475. attr DB_Test dbplan_destination Köln HBF
  1476. attr DB_Test room OPNV
  1477. </code></ul>
  1478. </ul>
  1479. <br>
  1480. <a name="DBPlanset"></a>
  1481. <b>Set-Commands</b><br>
  1482. <ul>
  1483. <li><b>interval</b></li>
  1484. set new interval time in seconds for parsing the DB time table<br>
  1485. <li><b>timeOffset</b></li>
  1486. Start of search: actual time plus time_offset.<br>
  1487. <li><b>reread</b></li>
  1488. reread and parse the DB time table. Only active, if not DevState: disabled<br>
  1489. <li><b>stop</b></li>
  1490. stop interval timer, only active if DevState: active<br>
  1491. <li><b>start</b></li>
  1492. restart interval timer, only active if DevState: stopped<br>
  1493. </ul>
  1494. <br>
  1495. <a name="DBPlanget"></a>
  1496. <b>Get-Commands</b><br>
  1497. <ul>
  1498. <li><b>PlainText</b></li>
  1499. the informations will be shown as plain text<br>
  1500. <li><b>searchStation</b></li>
  1501. search for a german DB Station. Without search pattern all stations will be shown.<br>
  1502. </ul>
  1503. <br>
  1504. <a name="DBPlanattr"></a>
  1505. <b>Attributes</b><br><br>
  1506. <ul>
  1507. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  1508. <br>
  1509. <li><b>dbplan_station</b></li>
  1510. place of departure<br>
  1511. <li><b>dbplan_destination</b></li>
  1512. place of destination<br>
  1513. <li><b>dbplan_via_1</b></li>
  1514. DB first via station<br>
  1515. <li><b>dbplan_via_2</b></li>
  1516. DB second via station<br>
  1517. <li><b>dbplan_journey_prod</b></li>
  1518. DB travel products like: ICE<br>
  1519. <li><b>dbplan_journey_opt</b></li>
  1520. DB journey options like: direct connection<br>
  1521. <li><b>dbplan_tariff_class</b></li>
  1522. DB tariff class: 1 or 2 class<br>
  1523. <li><b>dbplan_board_type</b></li>
  1524. DB board type: departure or arrival (depart / arrive)<br>
  1525. <li><b>dbplan_delayed_Journey</b></li>
  1526. DB delayed journey: on or off<br>
  1527. <li><b>dbplan_max_Journeys</b></li>
  1528. Number of displayed train connections in the station view.<br>
  1529. <li><b>dbplan_reg_train</b></li>
  1530. The train designation, e.g. S for everything S- and streetcars, ICE all ICE or ICE with train number.<br>
  1531. <li><b>dbplan_travel_date</b></li>
  1532. Define the date of travel in dd.mm.yy. Default: actual date.<br>
  1533. <li><b>dbplan_travel_time</b></li>
  1534. Define the time of travel in hh:mm. Default: actual time.<br>
  1535. <li><b>dbplan_addon_options</b></li>
  1536. extended options like discribed in the api document: <li><a http://webcache.googleusercontent.com/search?q=cache:wzb_OlIUCBQJ:www.geiervally.lechtal.at/sixcms/media.php/1405/Parametrisierte%2520%25DCbergabe%2520Bahnauskunft(V%25205.12-R4.30c,%2520f%25FCr.pdf+&cd=3&hl=de&ct=clnk&gl=de
  1537. ">Parametrisierte Übergabe Bahnauskunft</a></li><br>
  1538. <br>
  1539. <li><b>Attributes controlling the behavior:</b></li>
  1540. <li><b>dbplan-disable</b></li>
  1541. If set to 1 polling of DB Url will be stopped, setting to 0 or deleting will activate polling<br>
  1542. <li><b>dbplan-reading-deselect </b></li>
  1543. deselecting of readings<br>
  1544. <li><b>dbplan-default-char</b></li>
  1545. Define a string which will be displayed if no information is available. Defaultstring: "none"<br>
  1546. When defined the special string "delete" the raeding will not be filled and is not available since an information excists<br>
  1547. When defined the special string "nochar" the raeding will be filled with " "<br>
  1548. <li><b>dbplan-tabel-headers</b></li>
  1549. internal attribute to change the header information used by HTML::TableExtract<br>
  1550. <li><b>dbplan-station-file</b></li>
  1551. Directory and name of the station table to be used: /opt/fhem/FHEM/deutschland_bhf.csv<br>
  1552. This table is to be used as a help for the search for railway stations and has no other function in the module.<br>
  1553. <li><b>dbplan-base-type</b></li>
  1554. Select whether a station table (table) or a timetable (plan) display is to be generated<br>
  1555. <br>
  1556. <li><b>HTTPMOD attributes, have a look at the documentation</b></li>
  1557. <li><b>dbplan-remote-timeout</b></li>
  1558. <li><b>dbplan-remote-noshutdown</b></li>
  1559. <li><b>dbplan-remote-loglevel</b></li>
  1560. <li><b>dbplan-remote-buf</b></li>
  1561. </ul>
  1562. <br>
  1563. <a name="DBPlanReadings"></a>
  1564. <b>Readings</b><br><br>
  1565. <ul>
  1566. <li><a href="#internalReadings">internalReadings</a></li>
  1567. <br>
  1568. <li><b>plan_departure_(1..3)</b></li>
  1569. time of departure<br>
  1570. <li><b>plan_arrival_(1..3)</b></li>
  1571. time of arrival<br>
  1572. <li><b>plan_connection_(1..3)</b></li>
  1573. type of connection<br>
  1574. <li><b>plan_departure_delay_(1..3)</b></li>
  1575. delay time for departure<br>
  1576. <li><b>plan_arrival_delay_(1..3)</b></li>
  1577. delay time for arrival<br>
  1578. <li><b>plan_travel_duration_(1..3)</b></li>
  1579. travel duration time<br>
  1580. <li><b>plan_travel_change_(1..3)</b></li>
  1581. travel plattform changings<br>
  1582. <br>
  1583. <li><b>travel_departure_(1..3)</b></li>
  1584. informations about the departure and the plattform, if available<br>
  1585. <li><b>travel_destination_(1..3)</b></li>
  1586. informations about the destination and the plattform, if available<br>
  1587. <li><b>travel_price_(1..3)</b></li>
  1588. travel price in EUR<br>
  1589. <br>
  1590. <li><b>travel_error_(1..3)</b></li>
  1591. error information when calling the note url<br>
  1592. <li><b>travel_note_(1..3)</b></li>
  1593. travel note for travel plan<br>
  1594. <li><b>travel_note_link_(1..3)</b></li>
  1595. travel note link for further informations<br>
  1596. <li><b>travel_note_text_(1..3)</b></li>
  1597. travel note text<br>
  1598. </ul>
  1599. </ul>
  1600. =end html
  1601. =begin html_DE
  1602. <a name="DBPlan"></a>
  1603. <h3>DBPlan</h3>
  1604. <ul>
  1605. Das Modul holt von der Infoseite der DB <https://reiseauskunft.bahn.de/bin/query.exe/dox?S=departure&Z=destination&start=1&rt=1>
  1606. aktuelle Informationen zu einer angegeben Verbindung und legt sie in Fhem readings ab.
  1607. Die Datei mit den IBNR-Codes und Stationen der Deutschen Bahn kann unter http://www.michaeldittrich.de/ibnr abgerufen werden.
  1608. <br><br>
  1609. <b>Prerequisites</b>
  1610. <ul>
  1611. <br>
  1612. <li>
  1613. Dieses Modul verwendet die nicht blockierende HTTP-Funktion HttpUtils_NonblockingGet von FHEM's HttpUtils in der aktuellen Version.<br>
  1614. Falls das Modul noch nicht in Ihrer Fhem-Umgebung vorhanden ist, aktualisieren Sie bitte FHEM über den Update Befehl.<br>
  1615. </li>
  1616. </ul>
  1617. <br>
  1618. Der device status (DevState):
  1619. <ul>
  1620. <li><b>initialized</b></li>
  1621. Das Device ist definiert, aber es wurde keine erfolgreichen Anfragen und Analysen durchgeführt<br>
  1622. Dieser Zustand wird auch beim Wechsel von <inactive> auf <active> und <disabled> auf <enabled> gesetzt<br>
  1623. <li><b>active</b></li>
  1624. Das Device arbeitet<br>
  1625. <li><b>stopped</b></li>
  1626. Der Device Time wurde angehalten. Ein reread ist jedoch möglich<br>
  1627. <li><b>disabled</b></li>
  1628. Das Device wurde deaktiviert.<br>
  1629. </ul>
  1630. <br>
  1631. <a name="DBPlandefine"></a>
  1632. <b>Define</b>
  1633. <ul>
  1634. <br>
  1635. <code>define &lt;name&gt; DBPlan &lt;Refresh interval in seconds [time offset in minutes]&gt;</code>
  1636. <br><br>
  1637. Das Modul holt nach angegebenen "Intervall"-Sekunden über die DB URL die Fahrpläne. Ist time_offset definiert werden
  1638. die Fahrpläne für die aktuelle Zeit plus Offset in Minuten gelesen.<br>
  1639. <br>
  1640. Example:<br>
  1641. <br>
  1642. <ul><code>define DBPlan_Test DBPlan 60</code></ul>
  1643. </ul>
  1644. <br>
  1645. <a name="DBPlanconfiguration"></a>
  1646. <b>Konfiguration von DBPlan</b><br><br>
  1647. <ul>
  1648. Beispiel für eine Fahrplanabfrage:<br><br>
  1649. <ul><code>
  1650. attr DB_Test dbplan_station Köln-Weiden West
  1651. attr DB_Test dbplan_destination Köln HBF
  1652. attr DB_Test room OPNV
  1653. </code></ul>
  1654. </ul>
  1655. <br>
  1656. <a name="DBPlanset"></a>
  1657. <b>Set-Commands</b><br>
  1658. <ul>
  1659. <li><b>interval</b></li>
  1660. setzen einer anderen Intervallzeit für das Holen und Parsen der DB Informationen<br>
  1661. <li><b>timeOffset</b></li>
  1662. Start der Suche: aktuelle Zeit plus time_offset.<br>
  1663. <li><b>reread</b></li>
  1664. Holen und Parsen der DB Informationen. Nur aktiv, wenn kein Status: disabled<br>
  1665. <li><b>stop</b></li>
  1666. Stoppt den Timer. Nur aktiv, wenn Status: active<br>
  1667. <li><b>start</b></li>
  1668. Neustart des Timers. Nur aktiv, wenn Status: stopped<br>
  1669. </ul>
  1670. <br>
  1671. <a name="DBPlanget"></a>
  1672. <b>Get-Commands</b><br>
  1673. <ul>
  1674. <li><b>PlainText</b></li>
  1675. Die ermittelten Informationen werden als "plain Text" ausgegeben<br>
  1676. <li><b>searchStation</b></li>
  1677. suche in der Bahnhofstabelle. Wird kein Suchbegriff eingegen, werden alle Bahnhöfe angezeigt.<br>
  1678. </ul>
  1679. <br>
  1680. <a name="DBPlanattr"></a>
  1681. <b>Attributes</b><br><br>
  1682. <ul>
  1683. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  1684. <br>
  1685. <li><b>dbplan_station</b></li>
  1686. Abfahrtsbahnhof / Haltestelle<br>
  1687. <li><b>dbplan_destination </b></li>
  1688. Ankunftsbahnhof / Haltestelle<br>
  1689. <li><b>dbplan_via_1 </b></li>
  1690. 1. Zwischenhalt in Bahnhof / Haltestelle<br>
  1691. <li><b>dbplan_via_2 </b></li>
  1692. 2. Zwischenhalt in Bahnhof / Haltestelle<br>
  1693. <li><b>dbplan_journey_prod </b></li>
  1694. Verkehrsmittel, wie z.B.: ICE, Bus, Straßenbahn<br>
  1695. <li><b>dbplan_journey_opt </b></li>
  1696. Reiseoptionen wie z.B.: direct connection<br>
  1697. <li><b>dbplan_tariff_class </b></li>
  1698. 1. oder 2. Klasse<br>
  1699. <li><b>dbplan_board_type </b></li>
  1700. Fahrplansuche bzw. Bahnhofsanzeige für Abfahrts- oder Ankunftszeit (depart / arrive).<br>
  1701. <li><b>dbplan_delayed_Journey </b></li>
  1702. Bei off werden nur pünktliche Verbindungen angezeigt.<br>
  1703. <li><b>dbplan_max_Journeys </b></li>
  1704. Anzahl der angezeigten Zugverbindungen in der Bahnhofsansicht.<br>
  1705. <li><b>dbplan_reg_train </b></li>
  1706. die Zugbezeichnung, z.B. S für alles was S- und Straßenbahnen angeht, ICE alle ICE oder ICE mit Zugnummer. Usw.<br>
  1707. <li><b>dbplan_travel_date </b></li>
  1708. Reisedatum in der Angabe: dd.mm.yy<br>
  1709. <li><b>dbplan_travel_time </b></li>
  1710. Abfahtrtszeit in der Angabe: hh.mm<br>
  1711. <li><b>dbplan_addon_options </b></li>
  1712. weitere Optionen, wie sie im API-Dokument der DB beschrieben sind.<br>
  1713. <br>
  1714. <li><b>Steuernde Attribute:</b></li>
  1715. <li><b>dbplan-disable </b></li>
  1716. Device aktivieren / deaktivieren (s. auch FHEM-Doku)<br>
  1717. <li><b>dbplan-reading-deselect </b></li>
  1718. delsektieren von Readings<br>
  1719. <li><b>dbplan-default-char </b></li>
  1720. Hinweis, der angezeigt wird, wenn keine Information für ein reading zur Verfügung steht.<br>
  1721. - "none" ist der Standardhinweis.<br>
  1722. Sofern folgende spezielle Einträge gemacht werden:
  1723. - "delete" nicht genutzte readings werden auch nicht angezeigt.<br>
  1724. - "nochar" das Reading wird mit leerem Inhalt angezeigt.<br>
  1725. <li><b>dbplan-tabel-headers </b></li>
  1726. internes Attribut um die Spaltenbezeichnungen für HTML::TableExtract<br>
  1727. <li><b>dbplan-station-file </b></li>
  1728. Pfad zur Bahnhofstabelle der Deutschen Bahn (evtl. nicht vollständig). Für Für andere Verkehrsunternehmen liegen keine Tabellen vor.<br>
  1729. Diese Tabelle ist als Hilfe für die Suche nach Bahnhöfen anzusehen und hat keine weitere Funktion im Modul.<br>
  1730. <li><b>dbplan-base-type </b></li>
  1731. Anzeige als Bahnhofstabelle (table) oder Verbindungsinformation (plan)<br>
  1732. <br>
  1733. <li><b>HTTPMOD Attribute, siehe entsprechende Doku</b></li>
  1734. <li><b>dbplan-remote-timeout</b></li>
  1735. <li><b>dbplan-remote-noshutdown</b></li>
  1736. <li><b>dbplan-remote-loglevel</b></li>
  1737. <li><b>dbplan-remote-buf</b></li>
  1738. </ul>
  1739. <br>
  1740. <a name="DBPlanReadings"></a>
  1741. <b>Readings</b><br><br>
  1742. <ul>
  1743. <li><a href="#internalReadings">internalReadings</a></li>
  1744. <br>
  1745. <li><b>plan_departure_(1..3) </b></li>
  1746. Abfahrtszeit<br>
  1747. <li><b>plan_arrival_(1..3) </b></li>
  1748. Ankunftszeit<br>
  1749. <li><b>plan_connection_(1..3) </b></li>
  1750. Verbindungstyp<br>
  1751. <li><b>plan_departure_delay_(1..3) </b></li>
  1752. Verspätung in der Abfahrtszeit<br>
  1753. <li><b>plan_arrival_delay_(1..3) </b></li>
  1754. Verspätung in der Ankunftszeit<br>
  1755. <li><b>plan_travel_duration_(1..3) </b></li>
  1756. Reisezeit<br>
  1757. <li><b>plan_travel_change_(1..3) </b></li>
  1758. Anzahl der Umstiege<br>
  1759. <br>
  1760. <li><b>travel_note_(1..3) </b></li>
  1761. Hinweise für die Verbindung<br>
  1762. <li><b>travel_note_link_(1..3) </b></li>
  1763. Link zu den weiteren Verbindungsinformationen<br>
  1764. <li><b>travel_note_text_(1..3) </b></li>
  1765. Verbindungshinweis<br>
  1766. <li><b>travel_note_error_(1..3) </b></li>
  1767. Fehlertext der Detailinformation<br>
  1768. <br>
  1769. <li><b>travel_departure_(1..3) </b></li>
  1770. Informationen über den Abfahtsbahnhof und das Ankunftsgleis<br>
  1771. <li><b>travel_destination_(1..3) </b></li>
  1772. Informationen über den Zielbahnhof und das Ankunftsgleis<br>
  1773. <li><b>travel_price_(1..3) </b></li>
  1774. Fahrpreis<br>
  1775. </ul>
  1776. </ul>
  1777. =end html_DE
  1778. =cut