98_fheminfo.pm 11 KB

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