98_help.pm 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. # $Id: 98_help.pm 15223 2017-10-10 10:14:24Z betateilchen $
  2. #
  3. package main;
  4. use strict;
  5. use warnings;
  6. use Data::Dumper;
  7. my $ret;
  8. sub CommandHelp;
  9. sub cref_internals;
  10. sub cref_search;
  11. sub cref_search_cmd;
  12. sub cref_fill_list;
  13. sub cref_findInfo;
  14. sub help_Initialize($$) {
  15. my %hash = ( Fn => "CommandHelp",
  16. Hlp => "[<moduleName>],get help (this screen or module dependent docu)",
  17. InternalCmds => cref_internals() );
  18. $cmds{help} = \%hash;
  19. cref_fill_list();
  20. }
  21. sub CommandHelp {
  22. my ($cl, $arg) = @_;
  23. my ($mod,$lang) = split(" ",$arg);
  24. $lang //= AttrVal('global','language','en');
  25. $lang = (lc($lang) eq 'de') ? '_DE' : '';
  26. if($mod) {
  27. $mod = "help" if($mod eq "?");
  28. $mod = $defs{$mod}->{TYPE} if( defined($defs{$mod}) && $defs{$mod}->{TYPE} );
  29. $mod = lc($mod);
  30. my $modPath = AttrVal('global','modpath','.');
  31. my $output = '';
  32. my $outputInfo = cref_findInfo($modPath,$mod);
  33. if($cmds{help}{InternalCmds} !~ m/$mod\,/) {
  34. my %mods;
  35. my @modDir = ("$modPath/FHEM");
  36. $mod = $cmds{$mod}{ModuleName} if defined($cmds{$mod}) && defined($cmds{$mod}{ModuleName});
  37. foreach my $modDir (@modDir) {
  38. eval { opendir(DH, $modDir); }; # || die "Cant open $modDir: $!\n";
  39. while(my $l = readdir DH) {
  40. next if($l !~ m/^\d\d_.*\.pm$/);
  41. my $of = $l;
  42. $l =~ s/.pm$//;
  43. $l =~ s/^[0-9][0-9]_//;
  44. $mods{lc($l)} = "$modDir/$of";
  45. }
  46. }
  47. return "Module $mod not found" unless defined($mods{$mod});
  48. # read commandref docu from file
  49. $output = cref_search($mods{$mod},$lang);
  50. unless($output) {
  51. $output = cref_search($mods{$mod},"");
  52. $output = "<br/><br/>Keine deutsche Hilfe gefunden!<br/>$output" if $output;
  53. }
  54. $output = "No help found for module: $mod" unless $output;
  55. $output = $outputInfo.$output;
  56. } else {
  57. $output = "<br/><b>Internal command:</b> $mod";
  58. my $i;
  59. my $f = "$modPath/docs/commandref_frame$lang.html";
  60. my $skip = 1;
  61. my ($err,@text) = FileRead({FileName => $f, ForceType => 'file'});
  62. return $err if $err;
  63. foreach my $l (@text) {
  64. if($l =~ m/^<a name=\"$mod\"/) {
  65. $skip = 0;
  66. } elsif($l =~ m/^<!-- $mod.end/) {
  67. $skip = 1;
  68. } elsif (!$skip) {
  69. $output .= $l;
  70. }
  71. }
  72. }
  73. if( $cl && $cl->{TYPE} eq 'telnet' ) { # telnet output
  74. $output =~ s/<br\s*\?>/\n/ig;
  75. $output =~ s/\s*<li>\s*/\n- /ig;
  76. $output =~ s/<\/?ul>/\n/ig;
  77. $output =~ s/<\/?[^>]+>//g;
  78. $output =~ s/&lt;/</g;
  79. $output =~ s/&gt;/>/g;
  80. $output =~ tr/ / /s;
  81. $output =~ s/\n\n\ /\n/g;
  82. $output =~ s/&nbsp;/ /g;
  83. $output =~ s/&auml;/ä/g;
  84. $output =~ s/&Auml;/Ä/g;
  85. $output =~ s/&ouml;/ö/g;
  86. $output =~ s/&Ouml;/Ö/g;
  87. $output =~ s/&uuml;/ü/g;
  88. $output =~ s/&Uuml;/Ü/g;
  89. $output =~ s/&szlig;/ß/g;
  90. $output =~ s/\n\s*\n\s*\n/\n\n\n/g;
  91. $output =~ s/^\s+//;
  92. $output =~ s/\s+$//;
  93. } else { # html output
  94. my $url_prefix;
  95. if(AttrVal('global','exclude_from_update','') =~ m/commandref/) {
  96. $url_prefix = "http://fhem.de/commandref$lang.html";
  97. } else {
  98. $url_prefix = "$FW_ME/docs/commandref$lang.html";
  99. }
  100. # replace <a href="#..."> tags with a
  101. # working real link to commandref
  102. $output =~ s,<a\s+href="#,<a target="_blank" href="$url_prefix#,g;
  103. $output = "<html>$output</html>";
  104. }
  105. return $output;
  106. } else { # mod
  107. # cref_search_cmd(undef);
  108. my $str = "Possible commands:\n\n" .
  109. "Command Parameter\n" .
  110. " Description\n" .
  111. "----------------------------------------------------------------------\n";
  112. for my $cmd (sort keys %cmds) {
  113. next if($cmd =~ m/header:command/);
  114. next if(!$cmds{$cmd}{Hlp});
  115. next if($cl && $cmds{$cmd}{ClientFilter} &&
  116. $cl->{TYPE} !~ m/$cmds{$cmd}{ClientFilter}/);
  117. my @a = split(",", $cmds{$cmd}{Hlp}, 2);
  118. # $a[0] =~ s/</&lt;/g;
  119. # $a[0] =~ s/>/&gt;/g;
  120. $a[1] //= "";
  121. $a[1] = " $a[1]";
  122. # $a[1] =~ s/</&lt;/g;
  123. # $a[1] =~ s/>/&gt;/g;
  124. $str .= sprintf("%-15s%-50s\n%s\n", $cmd, $a[0], $a[1]);
  125. }
  126. return $str;
  127. }
  128. }
  129. sub cref_internals {
  130. my $mod = "./docs/commandref_frame.html";
  131. my $output = "";
  132. my ($err,@text) = FileRead({FileName => $mod, ForceType => 'file'});
  133. return $err if $err;
  134. foreach my $l (@text) {
  135. if($l =~ m/(^<!-- )(.*)( end.*>)/) {
  136. $output .= "$2,";
  137. }
  138. }
  139. return $output;
  140. }
  141. sub cref_search {
  142. my ($mod,$lang) = @_;
  143. my $output = "";
  144. my $skip = 1;
  145. my ($err,@text) = FileRead({FileName => $mod, ForceType => 'file'});
  146. return $err if $err;
  147. foreach my $l (@text) {
  148. if($l =~ m/^=begin html$lang$/) {
  149. $skip = 0;
  150. } elsif($l =~ m/^=end html$lang$/) {
  151. $skip = 1;
  152. } elsif(!$skip) {
  153. $output .= "$l\n";
  154. if($l =~ m,INSERT_DOC_FROM: ([^ ]+)/([^ /]+) ,) {
  155. my ($dir, $re) = ($1, $2);
  156. if(opendir(DH, $dir)) {
  157. foreach my $file (grep { m/^$2$/ } readdir(DH)) {
  158. $output .= cref_search("$dir/$file", $lang);
  159. }
  160. closedir(DH);
  161. }
  162. }
  163. }
  164. }
  165. return $output;
  166. }
  167. sub cref_search_cmd {
  168. my $skip = 1;
  169. my $mod = "./docs/commandref_frame.html";
  170. my ($err,@text) = FileRead({FileName => $mod, ForceType => 'file'});
  171. return $err if $err;
  172. foreach my $l (@text) {
  173. if($l =~ m/<b>Fhem commands<\/b>/) {
  174. $skip = 0;
  175. } elsif($l =~ m/<\/ul>/) {
  176. $skip = 1;
  177. } elsif(!$skip && $l !~ m/<ul>/) {
  178. $l =~ s/\?\,help//;
  179. $l =~ s/<a.*">//;
  180. $l =~ s/<\/a>.*//;
  181. $l =~ s/ //g;
  182. unless (defined($cmds{$l}{Hlp}) && $cmds{$l}{Hlp}) {
  183. my %hash = ( Hlp => "use \"help $l\" for more help");
  184. $cmds{$l} = \%hash if $l;
  185. }
  186. }
  187. }
  188. foreach my $i (split(",",$cmds{help}{InternalCmds})) {
  189. my %hash = ( Hlp => "use \"help $i\" for more help");
  190. $cmds{$i} = \%hash if $i;
  191. }
  192. return;
  193. }
  194. sub cref_fill_list(){
  195. my %mods;
  196. my %modIdx;
  197. my @modDir = ("FHEM");
  198. foreach my $modDir (@modDir) {
  199. opendir(DH, $modDir) || die "Cant open $modDir: $!\n";
  200. while(my $l = readdir DH) {
  201. next if($l !~ m/^\d\d_.*\.pm$/);
  202. my $of = $l;
  203. $l =~ s/.pm$//;
  204. $l =~ s/^[0-9][0-9]_//;
  205. $mods{$l} = "$modDir/$of";
  206. $modIdx{$l} = "device";
  207. open(MOD, "$modDir/$of") || die("Cant open $modDir/$l");
  208. while(my $cl = <MOD>) {
  209. if($cl =~ m/^=item\s+(helper|command|device)/) {
  210. $modIdx{$l} = $1;
  211. last;
  212. }
  213. }
  214. close(MOD);
  215. }
  216. }
  217. foreach my $mod (sort keys %mods) {
  218. my %h = ( Fn => undef,
  219. Hlp => "Command $mod not loaded. Use \"help $mod\" for more help" );
  220. $cmds{$mod} = \%h if ( ($modIdx{$mod} eq "command") && !(defined($cmds{$mod})) );
  221. }
  222. }
  223. sub cref_findInfo {
  224. my ($modPath,$mod) = @_;
  225. my ($l,@line,$found,$text);
  226. my ($err,@text) = FileRead({FileName => "$modPath/MAINTAINER.txt", ForceType => 'file'});
  227. foreach $l (@text) {
  228. @line = split("[ \t][ \t]*", $l,3);
  229. $found = ($l =~ m/_$mod/i);
  230. last if ($found);
  231. }
  232. if($found) {
  233. $line[0]= (split("/",$line[0]))[1] if $line[0] =~ /\//;
  234. $line[2]= "no info" if ($line[2] =~ /http/ || !defined($line[2]));
  235. $text = "<br/><b>Module:</b> $line[0] ";
  236. $text .= "<b>Maintainer:</b> $line[1] ";
  237. $text .= "<b>Forum:</b> $line[2]\n";
  238. }
  239. $text //= '';
  240. return $text;
  241. }
  242. 1;
  243. =pod
  244. =item command
  245. =item summary show help for module or device
  246. =item summary_DE Hilfe f&uuml;r Module und Ger&auml;te
  247. =begin html
  248. <a name="help"></a>
  249. <h3>?, help</h3>
  250. <ul>
  251. <code>? [&lt;moduleName|deviceName&gt;] [<language>]</code><br/>
  252. <code>help [&lt;moduleName|deviceName&gt;] [<language>]</code><br/>
  253. <br/>
  254. <ul>
  255. <li>Returns a list of available commands, when called without a
  256. moduleName/deviceName.</li>
  257. <li>Returns a module dependent helptext, same as in commandref.</li>
  258. <li>language will be determined in following order:
  259. <ul>
  260. <li>valid parameter &lt;language&gt; given</li>
  261. <li>global attribute language</li>
  262. <li>nothing founde: return english</li>
  263. </ul>
  264. </li>
  265. </ul>
  266. </ul>
  267. =end html
  268. =begin html_DE
  269. <a name="help"></a>
  270. <h3>?, help</h3>
  271. <ul>
  272. <code>? [&lt;moduleName|deviceName&gt;] [<language>]</code><br/>
  273. <code>help [&lt;moduleName|deviceName&gt;] [<language>]</code><br/>
  274. <br>
  275. <ul>
  276. <li>Liefert eine Liste aller Befehle mit einer Kurzbeschreibung zur&uuml;ck.</li>
  277. <li>Falls moduleName oder deviceName spezifiziert ist, wird die modul-spezifische Hilfe
  278. aus commandref zur&uuml;ckgeliefert.</li>
  279. <li>Die anzuzeigende Sprache wird in folgender Reihenfolge bestimmt:
  280. <ul>
  281. <li>g&uuml;ltiger Parameter &lt;language&gt; beim Aufruf &uuml;bergeben</li>
  282. <li>globales Attribut language</li>
  283. <li>falls alles fehlt: englisch</li>
  284. </ul>
  285. </li>
  286. </ul>
  287. </ul>
  288. =end html_DE
  289. =cut