70_PT8005.pm 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  1. ########################################################################################
  2. #
  3. # PT8005.pm
  4. #
  5. # FHEM module to read the data from a PeakTech PT8005 sound level meter
  6. #
  7. # Prof. Dr. Peter A. Henning, 2014
  8. #
  9. # Version 1.3 - January 2014
  10. #
  11. # setup, set/get functions and attributes see HTML text at bottom
  12. #
  13. ########################################################################################
  14. #
  15. # This programm is free software; you can redistribute it and/or modify
  16. # it under the terms of the GNU General Public License as published by
  17. # the Free Software Foundation; either version 2 of the License, or
  18. # (at your option) any later version.
  19. #
  20. # The GNU General Public License can be found at
  21. # http://www.gnu.org/copyleft/gpl.html.
  22. # A copy is found in the textfile GPL.txt and important notices to the license
  23. # from the author is found in LICENSE.txt distributed with these scripts.
  24. #
  25. # This script is distributed in the hope that it will be useful,
  26. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  27. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  28. # GNU General Public License for more details.
  29. #
  30. ########################################################################################
  31. package main;
  32. use strict;
  33. use warnings;
  34. use Device::SerialPort;
  35. use vars qw{%attr %defs};
  36. sub Log($$);
  37. #-- globals on start
  38. my $freq ="db(A)"; # dB(A) or dB(C)
  39. my $speed="fast"; # response speed fast or slow
  40. my $mode ="normal"; # min/max/...
  41. my $range="50-100 dB"; # measurement range
  42. my $over =""; # over/underflow
  43. #-- arrays for averaging (max 60 values per hour)
  44. my @noisearr;
  45. my @timearr;
  46. my $arrind=0;
  47. my $arrmax=70;
  48. my @noisehour;
  49. my $noisenight="";
  50. my $noiseday="";
  51. #-- arrays for hourly values
  52. my @hourarr;
  53. #-- These we may get on request
  54. my %gets = (
  55. "present" => "",
  56. "reading" => "R",
  57. );
  58. #-- These occur in a pulldown menu as settable values
  59. my %sets = (
  60. "interval" => "T",
  61. "Min/Max"=> "",
  62. "off" => "O",
  63. "rec" => "",
  64. "speed" => "",
  65. "range" => "", # toggle the measurement range
  66. "auto" => "", # set the measurement range to auto
  67. "dBA/C" => "", # toggle the frequency curve
  68. "freq" => "" # set the frequency curve to a value db(A) or db(C)
  69. );
  70. #-- Single key commands to the PT8005
  71. my %SKC = ("Min/Max","\x11", "off","\x33", "rec","\x55", "speed","\x77", "range","\x88", "dBA/C","\x99");
  72. ########################################################################################
  73. #
  74. # PT8005_Initialize
  75. #
  76. # Parameter hash
  77. #
  78. ########################################################################################
  79. sub PT8005_Initialize ($) {
  80. my ($hash) = @_;
  81. $hash->{DefFn} = "PT8005_Define";
  82. $hash->{GetFn} = "PT8005_Get";
  83. $hash->{SetFn} = "PT8005_Set";
  84. # LogM, LogY = name of the monthly and yearly log file
  85. $hash->{AttrList}= "LogM LogY LimNight LimDay ".
  86. "loglevel ".
  87. $readingFnAttributes;
  88. }
  89. ########################################################################################
  90. #
  91. # PT8005_Define - Implements DefFn function
  92. #
  93. # Parameter hash, definition string
  94. #
  95. ########################################################################################
  96. sub PT8005_Define($$) {
  97. my ($hash, $def) = @_;
  98. my @a = split("[ \t][ \t]*", $def);
  99. return "Define the serial device as a parameter"
  100. if(@a != 3);
  101. my $dev = $a[2];
  102. Log 1, "PT8005 opening device $dev";
  103. my $pt8005_serport = new Device::SerialPort ($dev);
  104. return "PT8005 Can't open $dev: $!" if(!$pt8005_serport);
  105. Log 1, "PT8005 opened device $dev";
  106. $hash->{USBDev} = $pt8005_serport;
  107. sleep(1);
  108. $pt8005_serport->close();
  109. $hash->{DeviceName} = $dev;
  110. $hash->{interval} = 60; # call every 60 seconds
  111. $modules{PT8005}{defptr}{$a[0]} = $hash;
  112. #-- InternalTimer blocks if init_done is not true
  113. my $oid = $init_done;
  114. $init_done = 1;
  115. readingsSingleUpdate($hash,"state","initialized",1);
  116. PT8005_GetStatus($hash);
  117. $init_done = $oid;
  118. return undef;
  119. }
  120. #######################################################################################
  121. #
  122. # PT8005_Average - Average backwards over given period
  123. #
  124. # Parameter hash, secsincemidnight,period
  125. #
  126. ########################################################################################
  127. sub PT8005_Average($$$) {
  128. my ($hash, $secsincemidnight, $period) = @_;
  129. #-- max. 1 hour allowed
  130. if( $period>60*$arrmax ){
  131. Log 1,"PT8005_Average: wrong averaging period $period, must be <= ".(60*$arrmax);
  132. return "";
  133. }
  134. my ($minind,$cntind,$oldtime,$ia,$ib,$fa,$fb,$ta,$tb,$fd,$avdata);
  135. #-- go backwards until we have period covered (=max. arrmax values)
  136. $minind=$arrind-1;
  137. $cntind=1;
  138. $minind+=$arrmax if($minind<0);
  139. $oldtime = $timearr[$minind];
  140. #-- no average if the previous time is undefined
  141. if( (!defined($oldtime)) || !($oldtime>0) ){
  142. Log 4,"PT8005_Average: invalid measurement at index $minind, no average possible";
  143. return "";
  144. }
  145. $oldtime-=86400 if($oldtime > $secsincemidnight);
  146. while( $oldtime > ($secsincemidnight-$period) ){
  147. #Log 1,"===>index $minind is ".($secsincemidnight-$timearr[$minind])." ago";
  148. $minind--;
  149. $minind+=$arrmax if($minind<0);
  150. $oldtime = $timearr[$minind];
  151. #-- no average if the previous time is undefined
  152. if( (!defined($oldtime)) || !($oldtime>0) ){
  153. Log 4,"PT8005_Average: invalid measurement at index $minind, no average possible";
  154. return "";
  155. }
  156. $oldtime-=86400 if($oldtime > $secsincemidnight);
  157. $cntind++;
  158. if( $cntind > $arrmax) {
  159. $cntind=$arrmax;
  160. Log 4,"PT8005_Average: ERROR, cntind > $arrmax";
  161. last;
  162. }
  163. }
  164. #-- now go forwards
  165. #-- first value must be done by hand
  166. $ia = $minind;
  167. $ib = $minind+1;
  168. $ib-=$arrmax if($ib>=$arrmax);
  169. $fa = $noisearr[$ia];
  170. $fb = $noisearr[$ib];
  171. $ta = $timearr[$ia];
  172. $ta-= 86400 if($ta > $secsincemidnight);
  173. $tb = $timearr[$ib];
  174. $tb-= 86400 if($tb > $secsincemidnight);
  175. $fd = $fa + ($fb-$fa)*($secsincemidnight-$period - $ta)/($tb - $ta);
  176. $avdata = ($fd + $fb)/2 * ($tb - ($secsincemidnight-$period));
  177. #Log 1,"===> interpolated value for data point between $ia and $ib is $fd and avdata=$avdata (tb=$tb, ssm=$secsincemidnight)";
  178. #-- other values can be done automatically
  179. for( my $i=1; $i<$cntind; $i++){
  180. $ia = $minind+$i;
  181. $ia-= $arrmax if($ia>=$arrmax);
  182. $ib = $ia+1;
  183. $ib-= $arrmax if($ib>=$arrmax);
  184. $fa = $noisearr[$ia];
  185. $fb = $noisearr[$ib];
  186. $ta = $timearr[$ia];
  187. $ta-= 86400 if($ta > $secsincemidnight);
  188. $tb = $timearr[$ib];
  189. $tb-= 86400 if($tb > $secsincemidnight);
  190. $avdata += ($fa + $fb)/2 * ($tb - $ta);
  191. #Log 1,"===> adding a new interval between $ia and $ib, new avdata = $avdata (tb=$tb ta=$ta)";
  192. }
  193. #-- and now the average for the period
  194. $avdata = int($avdata/($period/10))/10;
  195. return $avdata;
  196. }
  197. #########################################################################################
  198. #
  199. # PT8005_Cmd - Write command to meter
  200. #
  201. # Parameter hash, cmd = command
  202. #
  203. ########################################################################################
  204. sub PT8005_Cmd ($$) {
  205. my ($hash, $cmd) = @_;
  206. my $res;
  207. my $dev= $hash->{DeviceName};
  208. my $name = $hash->{NAME};
  209. my $serport = new Device::SerialPort ($dev);
  210. if(!$serport) {
  211. Log GetLogLevel($name,1), "PT8005: Can't open $dev: $!";
  212. return undef;
  213. }
  214. $serport->reset_error();
  215. $serport->baudrate(9600);
  216. $serport->databits(8);
  217. $serport->parity('none');
  218. $serport->stopbits(1);
  219. $serport->handshake('none');
  220. $serport->write_settings;
  221. #-- calculate checksum and send
  222. #my $cmd="\x33";
  223. my $count_out = $serport->write($cmd);
  224. Log GetLogLevel($name,3), "PT8005 write failed\n" unless ($count_out);
  225. #-- sleeping 0.05 seconds
  226. select(undef,undef,undef,0.05);
  227. my ($count_in, $string_in) = $serport->read(4);
  228. #-- control
  229. #my ($i,$j,$k);
  230. #my $ans="receiving:";
  231. #for($i=0;$i<$count_in;$i++){
  232. # $j=int(ord(substr($string_in,$i,1))/16);
  233. # $k=ord(substr($string_in,$i,1))%16;
  234. # $ans.="byte $i = 0x$j$k\n";
  235. #}
  236. #Log 1, $ans;
  237. #-- sleeping 0.05 seconds
  238. select(undef,undef,undef,0.05);
  239. $serport->close();
  240. }
  241. ########################################################################################
  242. #
  243. # PT8005_Get - Implements GetFn function
  244. #
  245. # Parameter hash, argument array
  246. #
  247. ########################################################################################
  248. sub PT8005_Get ($@) {
  249. my ($hash, @a) = @_;
  250. #-- check syntax
  251. return "PT8005_Get needs exactly one parameter" if(@a != 2);
  252. my $name = $hash->{NAME};
  253. my $v;
  254. #-- get present
  255. if($a[1] eq "present") {
  256. $v = ($hash->{READINGS}{"state"}{VAL} =~ m/.*dB.*/) ? 1 : 0;
  257. return "$a[0] present => $v";
  258. }
  259. #-- current reading
  260. if($a[1] eq "reading") {
  261. $v = PT8005_GetStatus($hash);
  262. if(!defined($v)) {
  263. Log GetLogLevel($name,2), "PT8005_Get $a[1] error";
  264. return "$a[0] $a[1] => Error";
  265. }
  266. $v =~ s/[\r\n]//g; # Delete the NewLine
  267. } else {
  268. return "PT8005_Get with unknown argument $a[1], choose one of " . join(" ", sort keys %gets);
  269. }
  270. Log GetLogLevel($name,3), "PT8005_Get $a[1] $v";
  271. return "$a[0] $a[1] => $v";
  272. }
  273. #######################################################################################
  274. #
  275. # PT8005 - GetStatus - Called in regular intervals to obtain current reading
  276. #
  277. # Parameter hash
  278. #
  279. ########################################################################################
  280. sub PT8005_GetStatus ($) {
  281. my ($hash) = @_;
  282. my $name = $hash->{NAME};
  283. my ($bcd,$i,$j,$k);
  284. my $data = 0.0;
  285. my $nospeed = 1;
  286. my $norange = 1;
  287. my $nofreq = 1;
  288. my $nodata = 1;
  289. my $loop = 0;
  290. my $secsincemidnight;
  291. my $av15 = "";
  292. my $av60 = "";
  293. my $avnight = 0;
  294. my $avday = 0;
  295. my $avcnt = 0;
  296. my $svalue;
  297. my $lvalue;
  298. my $hvalue;
  299. #-- restart timer for updates
  300. RemoveInternalTimer($hash);
  301. InternalTimer(gettimeofday() + $hash->{interval}, "PT8005_GetStatus", $hash,1);
  302. #-- check if rec is really off
  303. PT8005_Unrec($hash);
  304. #-- Obtain the current reading
  305. my $res;
  306. my $dev= $hash->{DeviceName};
  307. my $serport = new Device::SerialPort ($dev);
  308. if(!$serport) {
  309. Log GetLogLevel($name,3), "PT8005_Read: Can't open $dev: $!";
  310. return undef;
  311. }
  312. $serport->reset_error();
  313. $serport->baudrate(9600);
  314. $serport->databits(8);
  315. $serport->parity('none');
  316. $serport->stopbits(1);
  317. $serport->handshake('none');
  318. $serport->write_settings;
  319. #-- switch into recording mode
  320. my $count_out = $serport->write($SKC{"rec"});
  321. Log GetLogLevel($name,3), "PT8005_GetStatus: Switch to REC failed" unless ($count_out);
  322. #-- sleeping some time
  323. select(undef,undef,undef,0.15);
  324. #-- loop for the data
  325. while ( ($nodata > 0) and ($loop <3) ){
  326. #my $string_in=PT8005_Read($hash);
  327. select(undef,undef,undef,0.02);
  328. my ($count_in, $string_in) = $serport->read(64);
  329. $loop++;
  330. #--find data items
  331. if( index($string_in,"\xA5\x02") != -1){
  332. $nospeed=0;
  333. $speed="fast";
  334. } elsif( index($string_in,"\xA5\x03") != -1){
  335. $nospeed=0;
  336. $speed="slow";
  337. }
  338. if( index($string_in,"\xA5\x04") != -1){
  339. $mode="max";
  340. }elsif( index($string_in,"\xA5\x05") != -1){
  341. $mode="min";
  342. }else{
  343. $mode="normal";
  344. }
  345. if( index($string_in,"\xA5\x10") != -1){
  346. $norange=0;
  347. $range="30-80 dB";
  348. }elsif( index($string_in,"\xA5\x20") != -1){
  349. $norange=0;
  350. $range="50-100 dB";
  351. }elsif( index($string_in,"\xA5\x30") != -1){
  352. $norange=0;
  353. $range="80-130 dB";
  354. }elsif( index($string_in,"\xA5\x40") != -1){
  355. $norange=0;
  356. $range="30-130 dB";
  357. }
  358. if( index($string_in,"\xA5\x07") != -1){
  359. $over="over";
  360. }elsif( index($string_in,"\xA5\x08") != -1){
  361. $over="under";
  362. }else{
  363. $over="";
  364. }
  365. if( index($string_in,"\xA5\x1B") == -1){
  366. $nofreq=0;
  367. $freq="dB(A)";
  368. } elsif ( index($string_in,"\xA5\x1C") != -1){
  369. $nofreq=0;
  370. $freq="dB(C)";
  371. }
  372. #-- time not needed
  373. #my $in_time = index($string_in,"\xA5\x06");
  374. #if( $in_time != -1 ){
  375. # $bcd=ord(substr($string_in,$in_time+2,1));
  376. # $hour=int($bcd/16)*10 + $bcd%16 - 20;
  377. # $bcd=ord(substr($string_in,$in_time+3,1));
  378. # $min = int($bcd/16)*10 + $bcd%16;
  379. # $bcd=ord(substr($string_in,$in_time+4,1));
  380. # $sec = int($bcd/16)*10 + $bcd%16;
  381. # $time=sprintf("%02d:%02d:%02d",$hour,$min,$sec);
  382. #} else {
  383. # $time="undef";
  384. # Log GetLogLevel($name,3),"PT8005_GetStatus: no time value obtained"
  385. #}
  386. #-- data value
  387. my $in_data = index($string_in,"\xA5\x0D");
  388. if( $in_data != -1){
  389. my $s1=substr($string_in,$in_data+2,1);
  390. my $s2=substr($string_in,$in_data+3,1);
  391. if( ($s1 ne "") && ($s2 ne "") ){
  392. $nodata = 0;
  393. $bcd=ord($s1);
  394. $data=(int($bcd/16)*10 + $bcd%16)*10;
  395. $bcd=ord($s2);
  396. $data+=(int($bcd/16)*10 + $bcd%16)*0.1;
  397. }
  398. }
  399. }
  400. #-- sleeping some time
  401. select(undef,undef,undef,0.01);
  402. #-- leave recording mode
  403. $count_out = $serport->write($SKC{"rec"});
  404. #-- sleeping some time
  405. select(undef,undef,undef,0.01);
  406. #--
  407. $serport->close();
  408. #-- could not find a value
  409. if( $nofreq==1 ){
  410. Log GetLogLevel($name,4), "PT8005_GetStatus: no dBA/C frequency curve value obtained";
  411. };
  412. if( $norange==1 ){
  413. Log GetLogLevel($name,4), "PT8005_GetStatus: no range value obtained";
  414. };
  415. if( $nospeed==1 ){
  416. Log GetLogLevel($name,4), "PT8005_GetStatus: no speed value obtained";
  417. };
  418. if( $nodata==1 ){
  419. Log GetLogLevel($name,4), "PT8005_GetStatus: no data value obtained";
  420. };
  421. #-- addnl. messages
  422. if( $over eq "over"){
  423. Log GetLogLevel($name,4), "PT8005_GetStatus: Range overflow";
  424. }elsif( $over eq "under" ){
  425. Log GetLogLevel($name,4), "PT8005_GetStatus: Range underflow";
  426. }
  427. #-- put into readings
  428. $hash->{READINGS}{"soundlevel"}{UNIT} = $freq
  429. if( $nofreq ==0 );
  430. $hash->{READINGS}{"soundlevel"}{UNITABBR} = $freq
  431. if( $nofreq ==0 );
  432. #-- testing for wrong data value
  433. if( $data <=30 ){
  434. $nodata=1;
  435. };
  436. #-- put into READINGS
  437. readingsBeginUpdate($hash);
  438. readingsBulkUpdate($hash,"speed",$speed)
  439. if( $nospeed ==0 );
  440. readingsBulkUpdate($hash,"mode",$mode);
  441. readingsBulkUpdate($hash,"range",$range)
  442. if( $norange ==0 );
  443. readingsBulkUpdate($hash,"overflow",$over);
  444. if( $nodata==0 ){
  445. my ($sec, $min, $hour, $day, $month, $year, $wday,$yday,$isdst) = localtime(time);
  446. $secsincemidnight = $hour*3600+$min*60+$sec;
  447. $noisearr[$arrind] = $data;
  448. $timearr[$arrind] = $secsincemidnight;
  449. #-- average last 15 minutes
  450. $av15 = PT8005_Average($hash,$secsincemidnight,900);
  451. #-- output
  452. if( $av15 ne "" ){
  453. $svalue = sprintf("%3.1f %s [av15 %3.1f %s]",$data,$freq,$av15,$freq);
  454. $lvalue = sprintf("%3.1f av15 %3.1f ",$data,$av15);
  455. }else{
  456. $svalue = sprintf("%3.1f %s",$data,$freq);
  457. $lvalue = sprintf("%3.1f",$data);
  458. }
  459. readingsBulkUpdate($hash,"state",$svalue);
  460. readingsBulkUpdate($hash,"soundlevel",$lvalue);
  461. #-- average last hour if hour is past
  462. my $oldtime = $timearr[
  463. $arrind>0 ? $arrind-1 : $arrmax-1];
  464. if( defined($oldtime) ){
  465. my $oldhour = int($oldtime/3600);
  466. if( ($hour == ($oldhour+1)) || ($hour == ($oldhour-23)) ){
  467. my $longav = PT8005_Average($hash,$secsincemidnight,3600+$min*60+$sec);
  468. my $shortav = PT8005_Average($hash,$secsincemidnight,$min*60+$sec);
  469. if( ($longav ne "") && ($shortav ne "") ){
  470. $av60 = ($longav*(3600+$min*60+$sec)-$shortav*($min*60+$sec))/3600;
  471. $noisehour[$hour]=int($av60*10)/10;;
  472. Log GetLogLevel($name,4),"PT8005 gives average for hour $oldhour as $av60";
  473. #-- output
  474. $hvalue = sprintf("%3.1f",$av60);
  475. readingsBulkUpdate($hash,"soundav60",$hvalue);
  476. #-- check if nightly or daily average
  477. if( $hour==6 ){
  478. $avnight = 0.0;
  479. $avcnt = 0;
  480. if( defined($noisehour[23])){
  481. $avnight += $noisehour[23];
  482. $avcnt++;
  483. }
  484. for( my $i=0;$i<=6;$i++ ){
  485. if( defined($noisehour[$i])){
  486. $avnight += $noisehour[$i];
  487. $avcnt++;
  488. }
  489. }
  490. if( $avcnt > 0){
  491. $noisenight = int($avnight/$avcnt*10)/10;
  492. Log GetLogLevel($name,1),"PT8005: Nightly average = $avnight from $avcnt values";
  493. #-- output
  494. $hvalue = sprintf("%3.1f %s",$noisenight,$freq);
  495. readingsBulkUpdate($hash,"soundavnight",$hvalue);
  496. } else {
  497. $noisenight = "";
  498. }
  499. } elsif( $hour==22 ){
  500. $avday = 0.0;
  501. $avcnt = 0;
  502. for( my $i=7;$i<=22;$i++ ){
  503. if( defined($noisehour[$i])){
  504. $avday += $noisehour[$i];
  505. $avcnt++;
  506. }
  507. }
  508. if( $avcnt > 0){
  509. $noiseday = int($avday/$avcnt*10)/10;
  510. Log GetLogLevel($name,1),"PT8005: Daily average = $avnight from $avcnt values";
  511. #-- output
  512. $hvalue = sprintf("%3.1f %s",$noiseday,$freq);
  513. readingsBulkUpdate($hash,"soundavday",$hvalue);
  514. } else {
  515. $noiseday = "";
  516. }
  517. $hvalue = sprintf("%3.1f %3.1f",$noisenight,$noiseday);
  518. readingsBulkUpdate($hash,"soundday",$hvalue);
  519. }
  520. } else {
  521. $noisehour[$hour]=undef;
  522. Log GetLogLevel($name,4),"PT8005 NOT calculating new hourly average";
  523. }
  524. }
  525. }
  526. $arrind++;
  527. $arrind-=$arrmax if($arrind>=$arrmax);
  528. }
  529. readingsEndUpdate($hash,1);
  530. }
  531. ########################################################################################
  532. #
  533. # PT8005_Set - Implements SetFn function
  534. #
  535. # Parameter hash, a = argument array
  536. #
  537. ########################################################################################
  538. sub PT8005_Set ($@) {
  539. my ($hash, @a) = @_;
  540. my $name = shift @a;
  541. my $res;
  542. #-- for the selector: which values are possible
  543. #return join(" ", sort keys %sets) if(@a != 2);
  544. return "PT8005_Set: With unknown argument $a[0], choose one of " . join(" ", sort keys %sets)
  545. if(!defined($sets{$a[0]}));
  546. my $dev= $hash->{DeviceName};
  547. #-- Set single key value
  548. for (keys %SKC){
  549. if( $a[0] eq "$_" ){
  550. Log GetLogLevel($name,1),"PT8005_Set called with arg $_";
  551. PT8005_Cmd($hash,$SKC{$_});
  552. }
  553. }
  554. #-- Set timer value
  555. if( $a[0] eq "interval" ){
  556. #-- only values >= 5 secs allowed
  557. if( $a[1] >= 5){
  558. $hash->{interval} = $a[1];
  559. $res = 1;
  560. } else {
  561. $res = 0;
  562. }
  563. }
  564. #-- Set frequency curve to db(A) or db(C)
  565. if( $a[0] eq "freq" ){
  566. my $freqn = $a[1];
  567. if ( (!defined($freqn)) || (($freqn ne "dB(A)") && ($freqn ne "dB(C)")) ){
  568. return "PT8005_Set $name ".join(" ",@a)." with missing parameter, must be dB(A) or dB(C) ";
  569. }
  570. if ( (($freq eq "dB(A)") && ($freqn eq "dB(C)")) ||
  571. (($freq eq "dB(C)") && ($freqn eq "dB(A)")) ){
  572. Log GetLogLevel($name,1),"PT8005_Set freq $freqn";
  573. $res=PT8005_Cmd($hash,$SKC{"dBA/C"});
  574. }
  575. }
  576. #-- Set measurement range to auto
  577. if( $a[0] eq "auto" ){
  578. if ($range eq "30-80 dB"){
  579. $res =PT8005_Cmd($hash,$SKC{"range"});
  580. select(undef,undef,undef,0.05);
  581. $res.=PT8005_Cmd($hash,$SKC{"range"});
  582. select(undef,undef,undef,0.05);
  583. $res.=PT8005_Cmd($hash,$SKC{"range"});
  584. }elsif ($range eq "50-100 dB"){
  585. $res =PT8005_Cmd($hash,$SKC{"range"});
  586. select(undef,undef,undef,0.05);
  587. $res.=PT8005_Cmd($hash,$SKC{"range"});
  588. }elsif ($range eq "80-130 dB"){
  589. $res=PT8005_Cmd($hash,$SKC{"range"});
  590. }
  591. Log GetLogLevel($name,1),"PT8005_Set auto";
  592. }
  593. Log GetLogLevel($name,3), "PT8005_Set $name ".join(" ",@a)." => $res";
  594. return "PT8005_Set $name ".join(" ",@a)." => $res";
  595. }
  596. ########################################################################################
  597. #
  598. # PT8005_Unrec - switch recording mode off
  599. #
  600. # Parameter hash
  601. #
  602. ########################################################################################
  603. sub PT8005_Unrec ($) {
  604. my ($hash) = @_;
  605. my $res;
  606. my $dev= $hash->{DeviceName};
  607. my $name = $hash->{NAME};
  608. my $serport = new Device::SerialPort ($dev);
  609. if(!$serport) {
  610. Log GetLogLevel($name,3), "PT8005_UnRec: Can't open $dev: $!";
  611. return undef;
  612. }
  613. $serport->reset_error();
  614. $serport->baudrate(9600);
  615. $serport->databits(8);
  616. $serport->parity('none');
  617. $serport->stopbits(1);
  618. $serport->handshake('none');
  619. $serport->write_settings;
  620. for(my $i = 0; $i < 3; $i++) {
  621. #-- read data and look if it is nonzero
  622. my ($count_in, $string_in) = $serport->read(1);
  623. if( $string_in eq "" ){
  624. $serport->close();
  625. Log GetLogLevel($name,4),"PT8005_UnRec: REC is off ";
  626. return 1;
  627. } else {
  628. #-- leave recording mode
  629. select(undef,undef,undef,0.02);
  630. my $count_out = $serport->write($SKC{"rec"});
  631. #-- sleeping some time
  632. select(undef,undef,undef,0.02);
  633. }
  634. }
  635. $serport->close();
  636. Log GetLogLevel($name,4),"PT8005_UnRec: REC cannot be turned off ";
  637. return 0;
  638. }
  639. 1;
  640. =pod
  641. =begin html
  642. <a name="PT8005"></a>
  643. <h3>PT8005</h3>
  644. <p>FHEM module to commmunicate with a PeakTech PT8005 soundlevel meter<br />
  645. </p>
  646. <h4>Example</h4>
  647. <p>
  648. <code>define pt8005 PT8005 /dev/ttyUSB0 </code>
  649. </p><br />
  650. <a name="PT8005define"></a>
  651. <h4>Define</h4>
  652. <p>
  653. <code>define &lt;name&gt; PT8005 &lt;device&gt; </code>
  654. <br /><br /> Define a PT8005 soundlevel meter</p>
  655. <ul>
  656. <li>
  657. <code>&lt;name&gt;</code>
  658. Serial device port
  659. </li>
  660. </ul>
  661. <a name="PT8005set"></a>
  662. <h4>Set</h4>
  663. <ul>
  664. <li><a name="pt8005_interval">
  665. <code>set &lt;name&gt; interval &lt;value&gt;</code>
  666. </a>
  667. <br />sets the time period between measurements in seconds (default
  668. is 60 seconds, minimum is 5 seconds).
  669. </li>
  670. <li><a name="pt8005_auto">
  671. <code>set &lt;name&gt; auto</code>
  672. </a>
  673. <br />set the measurement range to auto (30 -130 dB) (displayed in status)
  674. </li>
  675. <li><a name="pt8005_freq">
  676. <code>set &lt;name&gt; freq dB(A)|dB(C)</code>
  677. </a>
  678. <br />set frequency curve to A or C (displayed in status)
  679. </li>
  680. <li><a name="pt8005_rec">
  681. <code>set &lt;name&gt; rec</code>
  682. </a>
  683. <br />toggle the recording mode
  684. </li>
  685. <li><a name="pt8005_minmax">
  686. <code>set &lt;name&gt; Min/Max</code>
  687. </a>
  688. <br />toggle the display between min value, max value and running measurement (displayed in status)
  689. </li>
  690. <li><a name="pt8005_dBA/C">
  691. <code>set &lt;name&gt; dBA/C</code>
  692. </a>
  693. <br />toggle the frequency curve (displayed in status)
  694. </li>
  695. <li><a name="pt8005_off">
  696. <code>set &lt;name&gt; off</code>
  697. </a>
  698. <br />switch off the device
  699. </li>
  700. </ul>
  701. <br />
  702. <a name="PT8005get"></a>
  703. <h4>Get</h4>
  704. <ul>
  705. <li><a name="pt8005_reading">
  706. <code>get &lt;name&gt; reading</code></a>
  707. <br /> read all current data </li>
  708. <li><a name="pt8005_present">
  709. <code>get &lt;name&gt; present</code></a>
  710. <br /> 1 if device present, 0 if not </li>
  711. </ul>
  712. <br />
  713. <a name="PT8005attr"></a>
  714. <h4>Attributes</h4>
  715. <ul>
  716. <li>Standard attributes <a href="#alias">alias</a>, <a href="#comment">comment</a>, <a
  717. href="#event-on-update-reading">event-on-update-reading</a>, <a
  718. href="#event-on-change-reading">event-on-change-reading</a>, <a
  719. href="#stateFormat">stateFormat</a>, <a href="#room"
  720. >room</a>, <a href="#eventMap">eventMap</a>, <a href="#loglevel">loglevel</a>,
  721. <a href="#webCmd">webCmd</a></li>
  722. </ul>
  723. =end html
  724. =cut