11_OWX_CCC.pm 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. ########################################################################################
  2. #
  3. # OWX_CCC.pm
  4. #
  5. # FHEM module providing hardware dependent functions for the COC/CUNO interface of OWX
  6. #
  7. # Prof. Dr. Peter A. Henning
  8. #
  9. # $Id: 11_OWX_CCC.pm 16671 2018-04-29 05:06:35Z phenning $
  10. #
  11. ########################################################################################
  12. #
  13. # Provides the following methods for OWX
  14. #
  15. # Define
  16. # Detect
  17. # Alarms
  18. # Complex
  19. # Discover
  20. # Open
  21. # Close
  22. # Reopen
  23. # Init
  24. # Read
  25. # ReadLow
  26. # Ready
  27. # Reset
  28. # Verify
  29. # Write
  30. #
  31. ########################################################################################
  32. #
  33. # $hash->{DeviceName} =
  34. # $hash->{INTERFACE} = "COC/CUNO/CUBE";
  35. # $hash->{HWDEVICE} =
  36. # $hash->{TYPE} = "OWX";
  37. #
  38. ########################################################################################
  39. package OWX_CCC;
  40. use strict;
  41. use warnings;
  42. ########################################################################################
  43. #
  44. # Constructor
  45. #
  46. ########################################################################################
  47. sub new($) {
  48. my ($class,$hash) = @_;
  49. return bless {
  50. hash => $hash,
  51. #-- module version
  52. version => "7.11"
  53. }, $class;
  54. }
  55. ########################################################################################
  56. #
  57. # Define - Implements Define method
  58. #
  59. # Parameter def = definition string
  60. #
  61. # Return undef if ok, otherwise error message
  62. #
  63. ########################################################################################
  64. sub Define($) {
  65. my ($self,$def) = @_;
  66. my $hash = $self->{hash};
  67. my @a = split("[ \t][ \t]*", $def);
  68. #-- check syntax
  69. if(int(@a) < 3){
  70. return "OWX_CCC::Define Syntax error - must be define <name> OWX <cuno/coc/cube-device>"
  71. }
  72. my $name = $a[0];
  73. $hash->{NAME} = $name;
  74. my $dev = $a[2];
  75. $hash->{DeviceName} = $dev;
  76. #-- Second step in case of CUNO: See if we can open it
  77. my $msg = "OWX_CCC::Define COC/CUNO/CUBE device $dev";
  78. #-- hash des COC/CUNO
  79. my $hwdevice = $main::defs{$dev};
  80. if(!$hwdevice){
  81. main::Log3 $name,1, $msg." not defined";
  82. return $msg." not defined";
  83. }
  84. main::Log(1,$msg." defined");
  85. #-- store with OWX device
  86. $hash->{DeviceName} = $dev;
  87. $hash->{ASYNCHRONOUS} = 0;
  88. $hash->{INTERFACE} = "COC/CUNO/CUBE";
  89. $hash->{HWDEVICE} = $hwdevice;
  90. #-- loop for some time until the state is "Initialized"
  91. for(my $i=0;$i<6;$i++){
  92. last if( $hwdevice->{STATE} eq "Initialized");
  93. main::Log(1,"OWX_CCC::Define Waiting, at t=$i ".$dev." is still ".$hwdevice->{STATE});
  94. select(undef,undef,undef,3);
  95. }
  96. main::Log(1, "OWX_CCC::Define Can't open ".$dev) if( $hwdevice->{STATE} ne "Initialized");
  97. #-- reset the 1-Wire system in COC/CUNO/CUBE
  98. main::CUL_SimpleWrite($hwdevice, "Oi");
  99. main::Log3 $name,1,"OWX_CCC::Define warning: version ".$self->{version}." not identical to OWX version ".$main::owx_version
  100. if( $self->{version} ne $main::owx_version);
  101. #-- call low level init function for the device
  102. $self->Init();
  103. return undef;
  104. }
  105. ########################################################################################
  106. #
  107. # Detect - Find out if we have the proper interface
  108. #
  109. # Return 1 if ok, otherwise 0
  110. #
  111. ########################################################################################
  112. sub Detect () {
  113. my ($self) = @_;
  114. my $hash = $self->{hash};
  115. my ($ret,$ress);
  116. my $name = $hash->{NAME};
  117. my $ress0 = "OWX_CCC::Detect: 1-Wire bus $name interface ";
  118. $ress = $ress0;
  119. #-- get the interface
  120. my $interface;
  121. my $hwdevice = $hash->{HWDEVICE};
  122. select(undef,undef,undef,2);
  123. #-- type of interface
  124. main::CUL_SimpleWrite($hwdevice, "V");
  125. select(undef,undef,undef,0.01);
  126. my ($err,$ob) = ReadLow($hwdevice);
  127. #my $ob = CallFn($owx_hwdevice->{NAME}, "GetFn", $owx_hwdevice, (" ", "raw", "V"));
  128. #-- process result for detection
  129. if( !defined($ob)){
  130. $ob="";
  131. $ret=0;
  132. #-- COC
  133. }elsif( $ob =~ m/.*CSM.*/){
  134. $interface="COC";
  135. $ress .= "DS2482 / COC detected in $hwdevice->{NAME}";
  136. $ret=1;
  137. #-- CUNO
  138. }elsif( $ob =~ m/.*CUNO.*/){
  139. $interface="CUNO";
  140. $ress .= "DS2482 / CUNO detected in $hwdevice->{NAME}";
  141. $ret=1;
  142. #-- CUBE
  143. }elsif( $ob =~ m/.*CUBE.*/){
  144. $interface="CUBE";
  145. $ress .= "DS2482 / CUBE detected in $hwdevice->{NAME}";
  146. $ret=1;
  147. #-- something else
  148. } else {
  149. $ret=0;
  150. }
  151. #-- treat the failure cases
  152. if( $ret == 0 ){
  153. $interface=undef;
  154. $ress .= "in $hwdevice->{NAME} could not be addressed, return was $ob";
  155. }
  156. #-- store with OWX device
  157. $hash->{INTERFACE} = $interface;
  158. main::Log(1, $ress);
  159. return $ret;
  160. }
  161. ########################################################################################
  162. #
  163. # Alarms - Find devices on the 1-Wire bus, which have the alarm flag set
  164. #
  165. # Return 0 because not implemented here.
  166. #
  167. ########################################################################################
  168. sub Alarms () {
  169. my ($self) = @_;
  170. return 0;
  171. }
  172. ########################################################################################
  173. #
  174. # Complex - Send match ROM, data block and receive bytes as response
  175. #
  176. # Parameter dev = ROM ID of device
  177. # data = string to send
  178. # numread = number of bytes to receive
  179. #
  180. # Return response, if OK
  181. # 0 if not OK
  182. #
  183. ########################################################################################
  184. sub Complex ($$$) {
  185. my ($self,$dev,$data,$numread) =@_;
  186. my $hash = $self->{hash};
  187. my $select;
  188. my $res = "";
  189. #-- get the interface
  190. my $hwdevice = $hash->{HWDEVICE};
  191. my $name = $hash->{NAME};
  192. #-- has match ROM part
  193. if( $dev ){
  194. #-- ID of the device
  195. my $owx_rnf = substr($dev,3,12);
  196. my $owx_f = substr($dev,0,2);
  197. #-- 8 byte 1-Wire device address
  198. my @rom_id =(0,0,0,0 ,0,0,0,0);
  199. #-- from search string to reverse string id
  200. $dev=~s/\.//g;
  201. for(my $i=0;$i<8;$i++){
  202. $rom_id[7-$i]=substr($dev,2*$i,2);
  203. }
  204. $select=sprintf("Om%s%s%s%s%s%s%s%s",@rom_id);
  205. main::Log3 $name,5,"OWX_CCC::Complex: sending match ROM to COC/CUNO ".$select;
  206. #--
  207. main::CUL_SimpleWrite($hwdevice, $select);
  208. my ($err,$ob) = ReadLow($hwdevice);
  209. #-- padding first 9 bytes into result string, since we have this
  210. # in the serial interfaces as well
  211. $res .= "000000000";
  212. }
  213. #-- has data part
  214. if ( $data ){
  215. $self->Write($data,0);
  216. $res .= $data;
  217. }
  218. #-- has receive part
  219. if( $numread > 0 ){
  220. #$numread += length($data);
  221. main::Log3 $name,5,"OWX_CCC::Complex: COC/CUNO is expected to deliver $numread bytes";
  222. $res.=$self->Read($numread);
  223. }
  224. return $res;
  225. }
  226. ########################################################################################
  227. #
  228. # Discover - Discover devices on the 1-Wire bus via internal firmware
  229. #
  230. # Return 0 : error
  231. # 1 : OK
  232. #
  233. ########################################################################################
  234. sub Discover () {
  235. my ($self) = @_;
  236. my $hash = $self->{hash};
  237. my $res;
  238. #-- get the interface
  239. my $hwdevice = $hash->{HWDEVICE};
  240. my $name = $hash->{NAME};
  241. #-- zero the array
  242. @{$hash->{DEVS}}=();
  243. #-- reset the busmaster
  244. $self->Init();
  245. #-- get the devices
  246. main::CUL_SimpleWrite($hwdevice, "Oc");
  247. select(undef,undef,undef,0.5);
  248. my ($err,$ob) = ReadLow($hwdevice);
  249. if( $ob ){
  250. # main::Log(3,"OWX_CCC::Discover: ".$hwdevice->{NAME}." device search returns ".$ob);
  251. foreach my $dx (split(/\n/,$ob)){
  252. next if ($dx !~ /^\d\d?\:[0-9a-fA-F]{16}/);
  253. $dx =~ s/\d+\://;
  254. my $ddx = substr($dx,14,2).".";
  255. #-- reverse data from culfw
  256. for( my $i=1;$i<7;$i++){
  257. $ddx .= substr($dx,14-2*$i,2);
  258. }
  259. $ddx .= ".".substr($dx,0,2);
  260. push (@{$hash->{DEVS}},$ddx);
  261. }
  262. return 1;
  263. } else {
  264. main::Log3 $name,1, "OWX_CCC::Discover No answer to device search";
  265. return 0;
  266. }
  267. }
  268. ########################################################################################
  269. #
  270. # Open - Open Device
  271. #
  272. ########################################################################################
  273. sub Open () {
  274. my ($self) = @_;
  275. my $hash = $self->{hash};
  276. }
  277. ########################################################################################
  278. #
  279. # Close - Close Device
  280. #
  281. ########################################################################################
  282. sub Close () {
  283. my ($self) = @_;
  284. my $hash = $self->{hash};
  285. }
  286. ########################################################################################
  287. #
  288. # Reopen - Reopen Device
  289. #
  290. ########################################################################################
  291. sub Reopen () {
  292. main::Log 1,"[OWX_CCC] Warning: ->Reopen currently not defined";
  293. }
  294. ########################################################################################
  295. #
  296. # Init - Low Level Init of the 1-wire device
  297. #
  298. # Return 1 : OK
  299. # 0 : not OK
  300. #
  301. ########################################################################################
  302. sub Init () {
  303. my ($self) = @_;
  304. my $hash = $self->{hash};
  305. my $dev = $hash->{DeviceName};
  306. my $name = $hash->{NAME};
  307. #main::Log3 $name,1,"OWX_CCC::Init called on device $dev for bus $name, state is ".$hash->{STATE};
  308. #-- get the interface
  309. my $hwdevice = $hash->{HWDEVICE};
  310. my $ob = main::CallFn($hwdevice->{NAME}, "GetFn", $hwdevice, (" ", "raw", "ORm"));
  311. #--
  312. # main::CUL_SimpleWrite($hwdevice, "ORm");
  313. # select(undef,undef,undef,0.01);
  314. # my ($err,$ob) = ReadLow($hwdevice);
  315. #main::Log3 $name,1,"OWX_CCC::Init gives ob=$ob ";
  316. if( !defined($ob) ){
  317. #main::Log 1,"empty ORm";
  318. return "empty ORm";
  319. }elsif( length($ob) < 13){
  320. #main::Log 1,"short ORm of length ".length($ob);
  321. return "short ORm of length ".length($ob);
  322. }elsif( substr($ob,9,4) eq "OK" ){
  323. #main::Log 1,"=====> OK";
  324. $hash->{STATE} = "opened";
  325. return undef;
  326. }else{
  327. #main::Log 1,"=====> ORm empty -> still OK ???";
  328. $hash->{STATE} = "opened";
  329. return undef;
  330. }
  331. }
  332. #######################################################################################
  333. #
  334. # Read - Implement the Read function
  335. #
  336. # Parameter numexp = expected number of bytes
  337. #
  338. #######################################################################################
  339. sub Read(@) {
  340. my ($self,$numexp) = @_;
  341. my $hash = $self->{hash};
  342. my $name = $hash->{NAME};
  343. my $state = $hash->{STATE};
  344. my $buffer = "";
  345. my $numget = 0;
  346. my ($err,$ob);
  347. my $try;
  348. my $maxtry = $numexp;
  349. #-- get the interface
  350. my $hwdevice = $hash->{HWDEVICE};
  351. for($try=0;$try<$maxtry;$try++){
  352. #main::Log(1, "Sending $hwdevice->{NAME}: OrB";
  353. #my $ob = CallFn($hwdevice->{NAME}, "GetFn", $hwdevice, (" ", "raw", "OrB"));
  354. main::CUL_SimpleWrite($hwdevice, "OrB");
  355. ($err,$ob) = main::CUL_ReadAnswer($hwdevice,$name,0,undef);
  356. #select(undef,undef,undef,0.01);
  357. #($err,$ob) = ReadLow($hwdevice);
  358. #-- process results
  359. if( !(defined($ob)) ){
  360. return "";
  361. #-- four bytes received makes one byte of result
  362. }elsif( length($ob) == 4 ){
  363. $buffer .= sprintf("%c",hex(substr($ob,0,2)));
  364. $numget++;
  365. #-- 11 bytes received makes one byte of result
  366. }elsif( length($ob) == 11 ){
  367. $buffer .= sprintf("%c",hex(substr($ob,9,2)));
  368. $numget++;
  369. #-- 18 bytes received from CUNO
  370. }elsif( length($ob) == 18 ){
  371. main::OWX_WDBGL($name,4,"OWX_CCC::Read 18 bytes from CUNO: ",$ob);
  372. #-- 20 bytes received = leftover from match
  373. }elsif( length($ob) == 20 ){
  374. $maxtry++;
  375. }else{
  376. main::Log3 $name,1,"OWX_CCC::Read unexpected number of ".length($ob)." bytes on bus ".$hwdevice->{NAME};
  377. }
  378. }
  379. if( $numget >= $numexp){
  380. main::OWX_WDBGL($name,4,"OWX_CCC::Read from CUNO with error=$err: ",$buffer);
  381. return $buffer
  382. #-- ultimate failure
  383. }else{
  384. main::Log3 $name, 1,"OWX_CCC::Read $name: $numget of $numexp bytes with error=$err at state $state, this is an unrecoverable error";
  385. #main::DevIo_Disconnected($hwdevice);
  386. return "";
  387. }
  388. }
  389. ########################################################################################
  390. #
  391. # ReadLow - Replacement for CUL_ReadAnswer for better control
  392. #
  393. # Parameter: hash = hash of bus master
  394. #
  395. # Return: string received
  396. #
  397. ########################################################################################
  398. sub ReadLow($)
  399. {
  400. my ($hwdevice) = @_;
  401. my $type = $hwdevice->{TYPE};
  402. my $name = $hwdevice->{NAME};
  403. my $arg ="";
  404. my $anydata=0;
  405. my $regexp =undef;
  406. my ($mculdata, $rin) = ("", '');
  407. my $buf;
  408. my $to = 3; # 3 seconds timeout
  409. $to = $hwdevice->{RA_Timeout} if($hwdevice->{RA_Timeout}); # ...or less
  410. for(;;) {
  411. return ("Device lost when reading answer for get $arg", undef)
  412. if(!$hwdevice->{FD});
  413. vec($rin, $hwdevice->{FD}, 1) = 1;
  414. my $nfound = select($rin, undef, undef, $to);
  415. if($nfound < 0) {
  416. next if ($! == EAGAIN() || $! == EINTR() || $! == 0);
  417. my $err = $!;
  418. #main::DevIo_Disconnected($hwdevice);
  419. main::Log 1,"============================> DISCOINNECTING";
  420. return("ReadLow $arg: $err", undef);
  421. }
  422. return ("Timeout reading answer for get $arg", undef)
  423. if($nfound == 0);
  424. $buf = main::DevIo_SimpleRead($hwdevice);
  425. return ("No data", undef) if(!defined($buf));
  426. if($buf) {
  427. main::Log3 $name,5, "OWX_CCC::ReadLow $buf";
  428. $mculdata .= $buf;
  429. }
  430. # \n\n is socat special
  431. if($mculdata =~ m/\r\n/ || $anydata || $mculdata =~ m/\n\n/ ) {
  432. if($regexp && $mculdata !~ m/$regexp/) {
  433. main::CUL_Parse($hwdevice, $hwdevice, $hwdevice->{NAME}, $mculdata, $hwdevice->{initString});
  434. } else {
  435. return (undef, $mculdata)
  436. }
  437. }
  438. }
  439. }
  440. ########################################################################################
  441. #
  442. # Ready - Implement the Ready function
  443. #
  444. # Return 1 : OK
  445. # 0 : not OK
  446. #
  447. ########################################################################################
  448. sub Ready () {
  449. my ($self) = @_;
  450. my $hash = $self->{hash};
  451. my $name = $hash->{NAME};
  452. my $success;
  453. $success = main::DevIo_OpenDev($hash,1,"main::OWX_Init")
  454. if($hash->{STATE} eq "disconnected");
  455. return $success;
  456. }
  457. ########################################################################################
  458. #
  459. # Reset - Reset the 1-Wire bus
  460. #
  461. # Return 1 : OK
  462. # 0 : not OK
  463. #
  464. ########################################################################################
  465. sub Reset () {
  466. my ($self) = @_;
  467. my $hash = $self->{hash};
  468. #-- get the interface
  469. my $hwdevice = $hash->{HWDEVICE};
  470. my $ob = main::CallFn($hwdevice->{NAME}, "GetFn", $hwdevice, (" ", "raw", "ORb"));
  471. if( substr($ob,9,4) eq "OK:1" ){
  472. return 1;
  473. }else{
  474. return 0
  475. }
  476. }
  477. ########################################################################################
  478. #
  479. # Verify - Verify a particular device on the 1-Wire bus
  480. #
  481. # Parameter dev = 8 Byte ROM ID of device to be tested
  482. #
  483. # Return 1 : device found
  484. # 0 : device not
  485. #
  486. ########################################################################################
  487. sub Verify ($) {
  488. my ($self,$dev) = @_;
  489. my $hash = $self->{hash};
  490. my $i;
  491. #-- get the interface
  492. my $hwdevice = $hash->{HWDEVICE};
  493. #-- Ask the COC/CUNO
  494. main::CUL_SimpleWrite($hwdevice, "OCf");
  495. #-- sleeping for some time
  496. select(undef,undef,undef,3);
  497. main::CUL_SimpleWrite($hwdevice, "Oc");
  498. select(undef,undef,undef,0.5);
  499. my ($err,$ob) = $self->($hwdevice);
  500. if( $ob ){
  501. foreach my $dx (split(/\n/,$ob)){
  502. next if ($dx !~ /^\d\d?\:[0-9a-fA-F]{16}/);
  503. $dx =~ s/\d+\://;
  504. my $ddx = substr($dx,14,2).".";
  505. #-- reverse data from culfw
  506. for( my $i=1;$i<7;$i++){
  507. $ddx .= substr($dx,14-2*$i,2);
  508. }
  509. $ddx .= ".".substr($dx,0,2);
  510. return 1 if( $dev eq $ddx);
  511. }
  512. }
  513. return 0;
  514. }
  515. #######################################################################################
  516. #
  517. # Write - Implement the write function
  518. #
  519. # Parameter cmd = string to be sent
  520. # reset = 1 if initial bus reset has to be done
  521. #
  522. ########################################################################################
  523. sub Write(@) {
  524. my ($self,$cmd, $reset) = @_;
  525. my $hash = $self->{hash};
  526. my $name = $hash->{NAME};
  527. my $count_out;
  528. if($hash->{STATE} eq "disconnected"){
  529. main::Log3 $name,4,"OWX_CCC::Write attempted to disconnected device $name";
  530. return undef;
  531. }
  532. #-- if necessary, perform a reset operation
  533. $self->Reset()
  534. if( $reset );
  535. my ($i,$j,$k);
  536. my $res = "";
  537. #-- get the interface
  538. my $hwdevice = $hash->{HWDEVICE};
  539. for( $i=0;$i<length($cmd);$i++){
  540. $j = int(ord(substr($cmd,$i,1))/16);
  541. $k = ord(substr($cmd,$i,1))%16;
  542. $res = sprintf "OwB%1x%1x ",$j,$k;
  543. main::CUL_SimpleWrite($hwdevice, $res);
  544. }
  545. main::OWX_WDBGL($name,5,"OWX_CCC::Write Sending out ",$cmd);
  546. }
  547. 1;
  548. =pod
  549. =item device
  550. =item summary to address an OWX interface device via COC/CUNO
  551. =item summary_DE zur Adressierung eines OWX Interface Device mit COC/CUNO
  552. =begin html
  553. <a name="OWX_CCC"></a>
  554. <h3>OWX_CCC</h3>
  555. <ul>
  556. See <a href="/fhem/docs/commandref.html#OWX">OWX</a>
  557. </ul>
  558. =end html
  559. =begin html_DE
  560. <a name="OWX_CCC"></a>
  561. <h3>OWX_CCC</h3>
  562. <ul>
  563. <a href="http://fhemwiki.de/wiki/Interfaces_f%C3%BCr_1-Wire">Deutsche Dokumentation im Wiki</a> vorhanden, die englische Version gibt es hier: <a href="/fhem/docs/commandref.html#OWX">OWX</a>
  564. </ul>
  565. =end html_DE
  566. =cut