OWX_FRM.pm 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. ########################################################################################
  2. #
  3. # OWX_FRM.pm
  4. #
  5. # FHEM module providing hardware dependent functions for the FRM interface of OWX
  6. #
  7. # Norbert Truchsess
  8. #
  9. # $Id: OWX_FRM.pm 6378 2014-08-07 22:01:18Z ntruchsess $
  10. #
  11. ########################################################################################
  12. #
  13. # Provides the following methods for OWX
  14. #
  15. # Define
  16. # Init
  17. # Verify #TODO refactor Verify...
  18. # search
  19. # alarms
  20. # execute
  21. #
  22. ########################################################################################
  23. package OWX_FRM;
  24. use strict;
  25. use warnings;
  26. #add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though...
  27. BEGIN {
  28. if ( !grep( /FHEM\/lib$/, @INC ) ) {
  29. foreach my $inc ( grep( /FHEM$/, @INC ) ) {
  30. push @INC, $inc . "/lib";
  31. };
  32. };
  33. };
  34. use Device::Firmata::Constants qw/ :all /;
  35. use Time::HiRes qw( gettimeofday );
  36. use ProtoThreads;
  37. no warnings 'deprecated';
  38. sub new() {
  39. my ($class) = @_;
  40. return bless {
  41. interface => "firmata",
  42. #-- module version
  43. version => 4.2
  44. }, $class;
  45. }
  46. sub Define($$) {
  47. my ( $self, $hash, $def ) = @_;
  48. my @a = split( "[ \t][ \t]*", $def );
  49. my $u = "wrong syntax: define <name> FRM_XXX pin";
  50. return $u unless int(@a) > 0;
  51. $self->{pin} = $a[2];
  52. $self->{id} = 0;
  53. $self->{name} = $hash->{NAME};
  54. $self->{hash} = $hash;
  55. return undef;
  56. }
  57. ########################################################################################
  58. #
  59. # Init - Initialize the 1-wire device
  60. #
  61. # Parameter hash = hash of bus master
  62. #
  63. # Return 1 or Errormessage : not OK
  64. # 0 or undef : OK
  65. #
  66. ########################################################################################
  67. sub initialize()
  68. {
  69. my ( $self ) = @_;
  70. main::LoadModule("FRM");
  71. my $pin = $self->{pin};
  72. my $hash = $self->{hash};
  73. my $ret = main::FRM_Init_Pin_Client( $hash, [$pin], PIN_ONEWIRE );
  74. die $ret if ( defined $ret );
  75. my $firmata = main::FRM_Client_FirmataDevice($hash);
  76. $firmata->observe_onewire( $pin, \&FRM_OWX_observer, $self );
  77. $self->{devs} = [];
  78. if ( main::AttrVal( $hash->{NAME}, "buspower", "" ) eq "parasitic" ) {
  79. $firmata->onewire_config( $pin, 1 );
  80. }
  81. $firmata->onewire_search($pin);
  82. return $self;
  83. }
  84. sub exit()
  85. {
  86. my ($self) = @_;
  87. #TODO implement deconfigure onewire in firmata.
  88. };
  89. sub FRM_OWX_observer
  90. {
  91. my ( $data, $self ) = @_;
  92. my $command = $data->{command};
  93. COMMAND_HANDLER: {
  94. $command eq "READ_REPLY" and do {
  95. $self->{responses}->{$data->{id}} = $data->{data}; # // $data->{device} // "defaultid"}
  96. main::Log3 ($self->{name},5,"FRM_OWX_observer: READ_REPLY $data->{id}: ".join " ",map sprintf("%02X",$_),@{$data->{data}}) if $self->{debug};
  97. last;
  98. };
  99. ( $command eq "SEARCH_REPLY" or $command eq "SEARCH_ALARMS_REPLY" ) and do {
  100. my @owx_devices = ();
  101. foreach my $device ( @{ $data->{devices} } ) {
  102. push @owx_devices, firmata_to_device($device);
  103. };
  104. if ( $command eq "SEARCH_REPLY" ) {
  105. $self->{devs} = \@owx_devices;
  106. main::Log3 ($self->{name},5,"FRM_OWX_observer: SEARCH_REPLY: ".join ",",@owx_devices) if $self->{debug};
  107. $self->{devs_timestamp} = gettimeofday();
  108. #TODO avoid OWX_ASYNC_AfterSearch to be called twice
  109. main::OWX_ASYNC_AfterSearch($self->{hash},\@owx_devices);
  110. } else {
  111. $self->{alarmdevs} = \@owx_devices;
  112. main::Log3 ($self->{name},5,"FRM_OWX_observer: SEARCH_ALARMS_REPLY: ".join ",",@owx_devices) if $self->{debug};
  113. $self->{alarmdevs_timestamp} = gettimeofday();
  114. #TODO avoid OWX_ASYNC_AfterAlarms to be called twice
  115. main::OWX_ASYNC_AfterAlarms($self->{hash},\@owx_devices);
  116. };
  117. last;
  118. };
  119. };
  120. main::OWX_ASYNC_RunTasks($self->{hash});
  121. };
  122. ########### functions implementing interface to OWX ##########
  123. sub device_to_firmata
  124. {
  125. my @device;
  126. foreach my $hbyte ( unpack "A2xA2A2A2A2A2A2xA2", shift ) {
  127. push @device, hex $hbyte;
  128. }
  129. return {
  130. family => shift @device,
  131. crc => pop @device,
  132. identity => \@device,
  133. }
  134. }
  135. sub firmata_to_device
  136. {
  137. my $device = shift;
  138. return sprintf( "%02X.%02X%02X%02X%02X%02X%02X.%02X", $device->{family}, @{ $device->{identity} }, $device->{crc} );
  139. }
  140. ########################################################################################
  141. #
  142. # factory methods for protothreads running discover, search, alarms and execute
  143. #
  144. ########################################################################################
  145. ########################################################################################
  146. #
  147. # Discover - Find devices on the 1-Wire bus
  148. #
  149. # Parameter hash = hash of bus master
  150. #
  151. # Return 1, if alarmed devices found, 0 otherwise.
  152. #
  153. ########################################################################################
  154. sub get_pt_discover() {
  155. my ($self) = @_;
  156. return PT_THREAD(sub {
  157. my ($thread) = @_;
  158. PT_BEGIN($thread);
  159. delete $self->{devs};
  160. main::FRM_Client_FirmataDevice($self->{hash})->onewire_search($self->{pin});
  161. main::OWX_ASYNC_TaskTimeout($self->{hash},gettimeofday+main::AttrVal($self->{name},"timeout",2));
  162. PT_WAIT_UNTIL(defined $self->{devs});
  163. PT_EXIT($self->{devs});
  164. PT_END;
  165. });
  166. }
  167. ########################################################################################
  168. #
  169. # Alarms - Find devices on the 1-Wire bus, which have the alarm flag set
  170. #
  171. # Return number of alarmed devices
  172. #
  173. ########################################################################################
  174. sub get_pt_alarms() {
  175. my ($self) = @_;
  176. return PT_THREAD(sub {
  177. my ($thread) = @_;
  178. PT_BEGIN($thread);
  179. delete $self->{alarmdevs};
  180. main::FRM_Client_FirmataDevice($self->{hash})->onewire_search_alarms($self->{pin});
  181. main::OWX_ASYNC_TaskTimeout($self->{hash},gettimeofday+main::AttrVal($self->{name},"timeout",2));
  182. PT_WAIT_UNTIL(defined $self->{alarmdevs});
  183. PT_EXIT($self->{alarmdevs});
  184. PT_END;
  185. });
  186. }
  187. sub get_pt_verify($) {
  188. my ($self,$dev) = @_;
  189. return PT_THREAD(sub {
  190. my ($thread) = @_;
  191. PT_BEGIN($thread);
  192. delete $self->{devs};
  193. main::FRM_Client_FirmataDevice($self->{hash})->onewire_search($self->{pin});
  194. main::OWX_ASYNC_TaskTimeout($self->{hash},gettimeofday+main::AttrVal($self->{name},"timeout",2));
  195. PT_WAIT_UNTIL(defined $self->{devs});
  196. PT_EXIT(scalar(grep {$dev eq $_} @{$self->{devs}}));
  197. PT_END;
  198. });
  199. }
  200. ########################################################################################
  201. #
  202. # Complex - Send match ROM, data block and receive bytes as response
  203. #
  204. # Parameter hash = hash of bus master,
  205. # owx_dev = ROM ID of device
  206. # data = string to send
  207. # numread = number of bytes to receive
  208. #
  209. # Return response, if OK
  210. # 0 if not OK
  211. #
  212. ########################################################################################
  213. sub get_pt_execute($$$$) {
  214. my ($self, $reset, $owx_dev, $writedata, $numread) = @_;
  215. return PT_THREAD(sub {
  216. my ($thread) = @_;
  217. PT_BEGIN($thread);
  218. if ( my $firmata = main::FRM_Client_FirmataDevice($self->{hash}) and my $pin = $self->{pin} ) {
  219. my @data = unpack "C*", $writedata if defined $writedata;
  220. my $id = $self->{id};
  221. my $ow_command = {
  222. 'reset' => $reset,
  223. 'skip' => defined($owx_dev) ? undef : 1,
  224. 'select' => defined($owx_dev) ? device_to_firmata($owx_dev) : undef,
  225. 'read' => $numread,
  226. 'write' => @data ? \@data : undef,
  227. 'delay' => undef,
  228. 'id' => $numread ? $id : undef
  229. };
  230. main::Log3 ($self->{name},5,"FRM_OWX_Execute: $id: $owx_dev [".join(" ",(map sprintf("%02X",$_),@data))."] numread: ".(defined $numread ? $numread : 0)) if $self->{debug};
  231. $firmata->onewire_command_series( $pin, $ow_command );
  232. if ($numread) {
  233. $thread->{id} = $id;
  234. $self->{id} = ( $id + 1 ) & 0xFFFF;
  235. delete $self->{responses}->{$id};
  236. main::OWX_ASYNC_TaskTimeout($self->{hash},gettimeofday+main::AttrVal($self->{name},"timeout",2));
  237. PT_WAIT_UNTIL(defined $self->{responses}->{$thread->{id}});
  238. my $ret = pack "C*", @{$self->{responses}->{$thread->{id}}};
  239. delete $self->{responses}->{$thread->{id}};
  240. PT_EXIT($ret);
  241. };
  242. };
  243. PT_END;
  244. });
  245. };
  246. sub poll() {
  247. my ( $self ) = @_;
  248. if ( my $frm = $self->{hash}->{IODev} ) {
  249. main::FRM_poll($frm);
  250. }
  251. };
  252. 1;