98_fheminfo.pm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. =for comment
  2. # $Id: 98_fheminfo.pm 16909 2018-06-25 15:29:01Z betateilchen $
  3. This script free software; you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation; either version 2 of the License, or
  6. any later version.
  7. The GNU General Public License can be found at
  8. http://www.gnu.org/copyleft/gpl.html.
  9. A copy is found in the textfile GPL.txt and important notices to the license
  10. from the author is found in LICENSE.txt distributed with these scripts.
  11. This script is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. GNU General Public License for more details.
  15. =cut
  16. package main;
  17. use strict;
  18. use warnings;
  19. use HttpUtils;
  20. my $c_system = 'system';
  21. my $c_noModel = 'noModel';
  22. my %fhemInfo = ();
  23. my @ignoreList = qw(Global);
  24. my @noModelList = qw(readingsgroup lacrosse zwdongle wol weekdaytimer
  25. cul_rfr solarview lw12 tscul knx dummy at archetype weather pushover twilight
  26. notify cloneDummy structure FHEMWEB hminfo readingsgroup);
  27. sub fheminfo_Initialize($$) {
  28. my %hash = (
  29. Fn => "CommandFheminfo",
  30. Hlp => "[send],show or send Fhem statistics",
  31. uri => "https://fhem.de/stats/statistics2.cgi",
  32. );
  33. $cmds{fheminfo} = \%hash;
  34. }
  35. sub CommandFheminfo($$) {
  36. my ($cl,$param) = @_;
  37. my @args = split("[ \t]+", $param);
  38. $args[0] = defined($args[0]) ? lc($args[0]) : "";
  39. my $doSend = ($args[0] eq 'send') ? 1 : 0;
  40. return "Unknown argument $args[0], usage: fheminfo [send]"
  41. if($args[0] ne "send" && $args[0] ne "");
  42. return "Won't send, as sendStatistics is set to 'never'."
  43. if($doSend && lc(AttrVal("global","sendStatistics","")) eq "never");
  44. _fi2_Count();
  45. if (defined($args[1]) && $args[1] eq 'debug') {
  46. $fhemInfo{$c_system}{'uniqueID'} = _fi2_shortId();
  47. return toJSON(\%fhemInfo);
  48. }
  49. _fi2_Send($cl) if $doSend;
  50. # do not return statistics data if called from update
  51. return "Statistics data sent to server. See Logfile (level 4) for details." unless defined($cl);
  52. return _fi2_TelnetTable($doSend) if ($cl && $cl->{TYPE} eq 'telnet');
  53. return _fi2_HtmlTable($doSend);
  54. }
  55. ################################################################
  56. # tools
  57. #
  58. sub _fi2_Count() {
  59. my $uniqueID = getUniqueId();
  60. my $os = $^O;
  61. my $perl = sprintf("%vd", $^V);
  62. %fhemInfo = ();
  63. $fhemInfo{$c_system}{'uniqueID'} = $uniqueID;
  64. $fhemInfo{$c_system}{'os'} = $os;
  65. $fhemInfo{$c_system}{'perl'} = $perl;
  66. $fhemInfo{$c_system}{'revision'} = _fi2_findRev();
  67. $fhemInfo{$c_system}{'configType'} = configDBUsed() ? 'configDB' : 'configFile';
  68. foreach my $key ( keys %defs )
  69. {
  70. # 1. skip if device is TEMPORARY or VOLATILE
  71. next if (defined($defs{$key}{'TEMPORARY'}) || defined($defs{$key}{'VOLATILE'}));
  72. my $name = $defs{$key}{NAME};
  73. my $type = $defs{$key}{TYPE};
  74. my $model = $c_noModel;
  75. # 2. look for model information in internals
  76. unless (lc($type) eq 'knx') {
  77. $model = defined($defs{$key}{model}) ? $defs{$key}{model} : $model;
  78. $model = defined($defs{$key}{MODEL}) ? $defs{$key}{MODEL} : $model;
  79. }
  80. if ($model eq $c_noModel) {
  81. # 3. look for model information in attributes
  82. $model = AttrVal($name,'model',$model);
  83. # 4. look for model information in readings
  84. $model = ReadingsVal($name,'model',$model);
  85. # special reading for BOSEST
  86. $model = ReadingsVal($name,'type',$model)
  87. if (lc($type) eq 'bosest');
  88. # special reading for ZWave
  89. if (lc($type) eq 'zwave') {
  90. $model = ReadingsVal($name,'modelId',undef);
  91. next unless (defined($model));
  92. next if ($model =~ /^0x.... /);
  93. $model = _fi2_zwave($model);
  94. }
  95. }
  96. # 5. ignore model for some modules
  97. foreach my $i (@noModelList) {
  98. $model = $c_noModel if (lc($type) eq $i);
  99. }
  100. # 6. check if model is a scalar
  101. $model = $c_noModel if (ref($model) eq 'HASH');
  102. # 7. skip for some special cases found in database
  103. next if ( ($model =~ /^unkno.*/i) ||
  104. ($model =~ /virtual.*/i) ||
  105. ($model eq '?') ||
  106. ($model eq '1') ||
  107. (defined($defs{$key}{'chanNo'})) ||
  108. ($name =~ m/^unknown_/) );
  109. # 8. finally count it :)
  110. $fhemInfo{$type}{$model}++ ;
  111. }
  112. # now do some more special handlings
  113. # add model info for configDB if used
  114. eval { $fhemInfo{'configDB'}{_cfgDB_type()}++ if configDBUsed(); };
  115. # delete all modules listed in ignoreList
  116. foreach my $i (@ignoreList) { delete $fhemInfo{$i}; }
  117. return;
  118. }
  119. sub _fi2_Send($) {
  120. my ($cl) = shift;
  121. $cl //= undef;
  122. my $sendType = defined($cl) ? 'nonblocking' : 'blocking';
  123. my $json = toJSON(\%fhemInfo);
  124. Log3("fheminfo",4,"fheminfo send ($sendType): $json");
  125. my %hu_hash = ();
  126. $hu_hash{url} = $cmds{fheminfo}{uri};
  127. $hu_hash{data} = "uniqueID=".$fhemInfo{$c_system}{'uniqueID'}."&json=$json";
  128. $hu_hash{header} = "User-Agent: FHEM";
  129. if (defined($cl)) {
  130. $hu_hash{callback} = sub($$$) {
  131. my ($hash, $err, $data) = @_;
  132. if($err) {
  133. Log 1, "fheminfo send: Server ERROR: $err";
  134. } else {
  135. Log3("fheminfo",4,"fheminfo send: Server RESPONSE: $data");
  136. }
  137. };
  138. HttpUtils_NonblockingGet(\%hu_hash);
  139. } else {
  140. my ($err, $data) = HttpUtils_BlockingGet(\%hu_hash);
  141. if($err) {
  142. Log 1, "fheminfo send: Server ERROR: $err";
  143. } else {
  144. Log3("fheminfo",4,"fheminfo send: Server RESPONSE: $data");
  145. }
  146. }
  147. return;
  148. }
  149. sub _fi2_TelnetTable($) {
  150. my ($doSend) = shift;
  151. my $str;
  152. $str .= "Following statistics data will be sent to server:\n(see Logfile level 4 for server response)\n\n" if($doSend == 1);
  153. $str .= "System Info\n";
  154. $str .= sprintf(" ConfigType%*s: %s\n",3," ",$fhemInfo{$c_system}{'configType'});
  155. $str .= sprintf(" SVN revision%*s: %s\n",0," ",$fhemInfo{$c_system}{'revision'})
  156. if (defined($fhemInfo{$c_system}{'revision'}));
  157. $str .= sprintf(" OS%*s: %s\n",11," ",$fhemInfo{$c_system}{'os'});
  158. $str .= sprintf(" Perl%*s: %s\n",9," ",$fhemInfo{$c_system}{'perl'});
  159. $str .= sprintf(" uniqueID%*s: %s\n",5," ",_fi2_shortId());
  160. my @keys = keys %fhemInfo;
  161. foreach my $type (sort @keys)
  162. {
  163. next if $type eq $c_system;
  164. $str .= "\nType: $type ";
  165. $str .= "Count: ".$fhemInfo{$type}{$c_noModel} if defined $fhemInfo{$type}{$c_noModel};
  166. $str .= "\n";
  167. while ( my ($model, $count) = each(%{$fhemInfo{$type}}) )
  168. { $str .= " $model = $fhemInfo{$type}{$model}\n" unless $model eq $c_noModel; }
  169. }
  170. return $str;
  171. }
  172. sub _fi2_HtmlTable($) {
  173. my ($doSend) = shift;
  174. my $result = "<html><table>";
  175. $result .= "<tr><td colspan='3'>Following statistics data will be sent to server:</br>(see Logfile level 4 for server response)</td></tr>" if($doSend == 1);
  176. $result .= "<tr><td><b>System Info</b></td></tr>";
  177. $result .= "<tr><td> </td><td>ConfigType:</td><td>$fhemInfo{$c_system}{'configType'}</td></tr>";
  178. $result .= "<tr><td> </td><td>SVN rev:</td><td>$fhemInfo{$c_system}{'revision'}</td></tr>"
  179. if (defined($fhemInfo{$c_system}{'revision'}));
  180. $result .= "<tr><td> </td><td>OS:</td><td>$fhemInfo{$c_system}{'os'}</td></tr>";
  181. $result .= "<tr><td> </td><td>Perl:</td><td>$fhemInfo{$c_system}{'perl'}</td></tr>";
  182. $result .= "<tr><td> </td><td>uniqueId:</td><td>"._fi2_shortId()."</td></tr>";
  183. $result .= "<tr><td colspan=3>&nbsp;</td></tr>";
  184. $result .= "<tr><td><b>Modules</b></td><td><b>Model</b></td><td><b>Count</b></td></tr>";
  185. my @keys = keys %fhemInfo;
  186. foreach my $type (sort @keys)
  187. {
  188. next if ($type eq $c_system);
  189. $fhemInfo{$type}{$c_noModel} //= '';
  190. $result .= "<tr><td>$type</td><td> </td><td>$fhemInfo{$type}{$c_noModel}</td></tr>";
  191. while ( my ($model, $count) = each(%{$fhemInfo{$type}}) )
  192. { $result .= "<tr><td> </td><td>$model</td><td>$fhemInfo{$type}{$model}</td></tr>" unless $model eq $c_noModel; }
  193. }
  194. $result .= "</table></html>";
  195. return $result;
  196. }
  197. sub _fi2_findRev() {
  198. my $cf = 'controls_fhem.txt';
  199. my $filename = (-e "./$cf") ? "./$cf" : AttrVal("global","modpath",".")."/FHEM/$cf";
  200. my ($err, @content) = FileRead({FileName => $filename, ForceType => "file"});
  201. return if $err;
  202. my (undef,$rev) = split (/ /,$content[0]);
  203. return $rev;
  204. }
  205. sub _fi2_zwave($) {
  206. my ($zwave) = @_;
  207. my ($mf, $prod, $id) = split(/-/,$zwave);
  208. ($mf, $prod, $id) = (lc($mf), lc($prod), lc($id)); # Just to make it sure
  209. my $xml = $attr{global}{modpath}.
  210. "/FHEM/lib/openzwave_manufacturer_specific.xml";
  211. my ($err,@data) = FileRead({FileName => $xml, ForceType=>'file'});
  212. return $err if($err);
  213. my ($lastMf, $mName, $ret) = ("","");
  214. foreach my $l (@data) {
  215. if($l =~ m/<Manufacturer.*id="([^"]*)".*name="([^"]*)"/) {
  216. $lastMf = lc($1);
  217. $mName = $2;
  218. next;
  219. }
  220. if($l =~ m/<Product type\s*=\s*"([^"]*)".*id\s*=\s*"([^"]*)".*name\s*=\s*"([^"]*)"/) {
  221. if($mf eq $lastMf && $prod eq lc($1) && $id eq lc($2)) {
  222. $ret = "$mName $3";
  223. last;
  224. }
  225. }
  226. }
  227. return $ret if($ret);
  228. return $zwave;
  229. }
  230. sub _fi2_shortId() {
  231. return substr($fhemInfo{$c_system}{'uniqueID'},0,3)."...";
  232. }
  233. 1;
  234. =pod
  235. =item command
  236. =item summary display information about the system and FHEM definitions
  237. =item summary_DE zeigt Systeminformationen an
  238. =begin html
  239. <a name="fheminfo"></a>
  240. <h3>fheminfo</h3>
  241. <ul>
  242. <code>fheminfo [send]</code>
  243. <br>
  244. <br>
  245. fheminfo displays information about the system and FHEM definitions.
  246. <br>
  247. <br>
  248. The optional parameter <code>send</code> transmitts the collected data
  249. to a central server in order to support the development of FHEM. <br/>
  250. The submitted data is processed graphically. The results can be viewed
  251. on <a href="https://fhem.de/stats/statistics.html">http://fhem.de/stats/statistics.html</a>.<br/>
  252. The IP address will not be stored in database, only used for region determination during send.
  253. <br>
  254. <br>
  255. Features:<br>
  256. <ul>
  257. <li>ConfigType (configDB|configFILE)</li>
  258. <li>SVN rev number</li>
  259. <li>Operating System Information</li>
  260. <li>Installed Perl version</li>
  261. <li>Defined modules</li>
  262. <li>Defined models per module</li>
  263. </ul>
  264. <br>
  265. <a name="fheminfoattr"></a>
  266. <b>Attributes</b>
  267. <br>
  268. <br>
  269. The following attributes are used only in conjunction with the
  270. <code>send</code> parameter. They are set on <code>attr global</code>.
  271. <br>
  272. <br>
  273. <ul>
  274. <li>sendStatistics<br>
  275. This attribute is used in conjunction with the <code>update</code> command.
  276. <br>
  277. <code>onUpdate</code>: transfer of data on every update (recommended setting).
  278. <br>
  279. <code>manually</code>: manually transfer of data via the <code>fheminfo send</code> command.
  280. <br>
  281. <code>never</code>: prevents transmission of data at anytime.
  282. </li>
  283. <br>
  284. </ul>
  285. </ul>
  286. =end html
  287. =cut