PageRenderTime 27ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/tags/v2-85/mh/lib/Serial_Item.pm

#
Perl | 757 lines | 425 code | 66 blank | 266 comment | 85 complexity | 708df66afb9e3896ec825d90a00561e8 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, GPL-3.0
  1. use strict;
  2. package Serial_Item;
  3. @Serial_Item::ISA = ('Generic_Item');
  4. my (%serial_items_by_id);
  5. sub reset {
  6. undef %serial_items_by_id; # Reset on code re-load
  7. }
  8. sub serial_items_by_id {
  9. my($id) = @_;
  10. return unless $serial_items_by_id{$id};
  11. return @{$serial_items_by_id{$id}};
  12. }
  13. # For backward compatability, return just the first item
  14. sub serial_item_by_id {
  15. my($id) = @_;
  16. my @refs = &serial_items_by_id($id);
  17. return $refs[0];
  18. }
  19. sub new {
  20. my ($class, $id, $state, $port_name) = @_;
  21. my $self = {state => undef}; # Use undef ... '' will return as defined
  22. # print "\n\nWarning: duplicate ID codes on different Serial_Item objects:\n " .
  23. # "id=$id state=$state states=@{${$serial_item_by_id{$id}}{states}}\n\n" if $serial_item_by_id{$id};
  24. $$self{port_name} = $port_name;
  25. &add($self, $id, $state);
  26. bless $self, $class;
  27. $self->set_interface($port_name) if $id and $id =~ /^X/;
  28. $self->state_overload('off'); # By default, do not process ~;: strings as substate/multistate
  29. return $self;
  30. }
  31. sub add {
  32. my ($self, $id, $state) = @_;
  33. # Allow for Serial_Item's without states
  34. # $state = 'default_state' unless defined $state;
  35. $state = $id unless defined $state;
  36. $$self{state_by_id}{$id} = $state if defined $id;
  37. $$self{id_by_state}{$state} = $id if defined $state;
  38. push(@{$$self{states}}, $state);
  39. push(@{$serial_items_by_id{$id}}, $self) if $id;
  40. }
  41. sub is_started {
  42. my ($self) = @_;
  43. my $port_name = $self->{port_name};
  44. return ($main::Serial_Ports{$port_name}{object}) ? 1 : 0;
  45. }
  46. sub is_stopped {
  47. my ($self) = @_;
  48. my $port_name = $self->{port_name};
  49. return ($main::Serial_Ports{$port_name}{object}) ? 0 : 1;
  50. }
  51. # Try to do a 'new' ... object is not kept, even if new is sucessful
  52. # - not sure if there is a better way to test if a port is available
  53. # Hopefully this is not too wasteful
  54. sub is_available {
  55. my ($self) = @_;
  56. my $port_name = $self->{port_name};
  57. my $port = $main::Serial_Ports{$port_name}{port};
  58. print "testing port $port_name $port ... ";
  59. my $sp_object;
  60. # Use the 2nd parm of '1' to indicate this do a test open
  61. # - Modified Win32::SerialPort so it does not compilain if New/open fails
  62. if (( $main::OS_win and $sp_object = new Win32::SerialPort($port, 1))or
  63. (!$main::OS_win and $sp_object = new Device::SerialPort($port))) {
  64. print " available\n";
  65. $sp_object->close;
  66. return 1;
  67. }
  68. else {
  69. print " not available\n";
  70. return 0;
  71. }
  72. }
  73. sub start {
  74. my ($self) = @_;
  75. my $port_name = $self->{port_name};
  76. print "Starting port $port_name on port $main::Serial_Ports{$port_name}{port}\n";
  77. if ($main::Serial_Ports{$port_name}{object}) {
  78. print "Port $port_name is already started\n";
  79. return;
  80. }
  81. if ($port_name) {
  82. if (&main::serial_port_open($port_name)) {
  83. print "Port $port_name was re-opened\n";
  84. }
  85. else {
  86. print "Serial_Item start failed for port $port_name\n";
  87. }
  88. }
  89. else {
  90. print "Error in Serial_Item start: no port name for object=$self\n";
  91. }
  92. }
  93. sub stop {
  94. my ($self) = @_;
  95. my $port_name = $self->{port_name};
  96. my $sp_object = $main::Serial_Ports{$port_name}{object};
  97. if ($sp_object) {
  98. my $port = $main::Serial_Ports{$port_name}{port};
  99. # &Win32::SerialPort::debug(1);
  100. if ($sp_object->close) {
  101. print "Port $port_name on port $port was closed\n";
  102. }
  103. else {
  104. print "Serial_Item stop failed for port $port_name\n";
  105. }
  106. # Delete the ports, even if it didn't close, so we can do
  107. # starts again without a 'port reuse' message.
  108. delete $main::Serial_Ports{$port_name}{object};
  109. delete $main::Serial_Ports{object_by_port}{$port};
  110. # &Win32::SerialPort::debug(0);
  111. }
  112. else {
  113. print "Error in Serial_Item stop for port $port_name: Port is not started\n";
  114. }
  115. }
  116. sub said {
  117. my $port_name = $_[0]->{port_name};
  118. my $datatype = $main::Serial_Ports{$port_name}{datatype};
  119. my $data;
  120. if ($datatype and $datatype eq 'raw') {
  121. $data = $main::Serial_Ports{$port_name}{data};
  122. $main::Serial_Ports{$port_name}{data} = undef;
  123. }
  124. else {
  125. $data = $main::Serial_Ports{$port_name}{data_record};
  126. $main::Serial_Ports{$port_name}{data_record} = undef; # Maybe this should be reset in main loop??
  127. }
  128. # print "db serial $port_name data: $data\n" if $main::Debug{$port_name};
  129. return $data;
  130. }
  131. sub set_data {
  132. my ($self, $data) = @_;
  133. my $port_name = $self->{port_name};
  134. my $datatype = $main::Serial_Ports{$port_name}{datatype};
  135. if ($datatype eq 'raw') {
  136. $main::Serial_Ports{$port_name}{data} = $data;
  137. }
  138. else {
  139. $main::Serial_Ports{$port_name}{data_record} = $data;
  140. }
  141. }
  142. sub set_receive {
  143. my ($self, $state, $set_by) = @_;
  144. # $set_by = 'serial' unless $set_by;
  145. return if &main::check_for_tied_filters($self, $state);
  146. return if &set_prev_pass_check($self, $state);
  147. &Generic_Item::set_states_for_next_pass($self, $state, $set_by);
  148. }
  149. sub set_dtr {
  150. my ($self, $state) = @_;
  151. my $port_name = $self->{port_name};
  152. if (my $serial_port = $main::Serial_Ports{$port_name}{object}) {
  153. $main::Serial_Ports{$port_name}{object}->dtr_active($state);
  154. print "Serial_port $port_name dtr set to $state\n" if $main::Debug{serial};
  155. }
  156. else {
  157. print "Error, serial port set_dtr for $port_name failed, port has not been set\n";
  158. }
  159. }
  160. sub set_rts {
  161. my ($self, $state) = @_;
  162. my $port_name = $self->{port_name};
  163. if (my $serial_port = $main::Serial_Ports{$port_name}{object}) {
  164. $main::Serial_Ports{$port_name}{object}->rts_active($state);
  165. print "Serial_port $port_name rts set to $state\n" if $main::Debug{serial};
  166. }
  167. else {
  168. print "Error, serial port set_rts for $port_name failed, port has not been set\n";
  169. }
  170. }
  171. sub set {
  172. my ($self, $state, $set_by) = @_;
  173. return if &main::check_for_tied_filters($self, $state);
  174. # Allow for Serial_Item's without states
  175. unless (defined $state) {
  176. print "Serial_Item set with an empty state on $$self{object_name}\n";
  177. $state = 'default_state';
  178. }
  179. my $serial_id;
  180. # Allow for upper/mixed case (e.g. treat ON the same as on ... so X10_Items is simpler)
  181. if (defined $self->{id_by_state}{$state}) {
  182. $serial_id = $self->{id_by_state}{$state};
  183. }
  184. elsif (defined $self->{id_by_state}{lc $state}) {
  185. $serial_id = $self->{id_by_state}{lc $state};
  186. }
  187. else {
  188. $serial_id = $state;
  189. }
  190. # uc since other mh processing can lc it to avoid state sensitivity
  191. my $serial_data = $serial_id;
  192. $serial_id = uc $serial_id unless defined $self->{states_casesensitive};
  193. return if &set_prev_pass_check($self, $serial_id);
  194. &Generic_Item::set_states_for_next_pass($self, $state, $set_by);
  195. my $port_name = $self->{port_name};
  196. my $interface = $self->{interface};
  197. $interface = '' unless $interface;
  198. print "Serial_Item: port=$port_name self=$self state=$state data=$serial_data interface=$$self{interface}\n"
  199. if $main::Debug{serial};
  200. return if $main::Save{mode} eq 'offline';
  201. return unless %main::Serial_Ports;
  202. # First deal with X10 strings. Assume X10 capable if interface is set.
  203. # Ideally, we would test for specific X10 interfaces, but so far it only
  204. # gets set if it is an X10 interface.
  205. if (($serial_data =~ /^X/ and $interface ne '') or $self->isa('X10_Item')) {
  206. # allow for xx% (e.g. 1% -> &P1)
  207. # ... need to allow for multiple X10 commands data here?
  208. if ($serial_data =~ /(\d+)%/) {
  209. $serial_data = '&P' . int ($1 * 63 / 100 + 0.5);
  210. }
  211. # Make sure that &P codes have the house_unit code prefixed
  212. # - e.g. device A1 -> A&P1
  213. if ($serial_data =~ /&P/) {
  214. $serial_data = substr($self->{x10_id}, 1, 1) . $serial_data;
  215. }
  216. # If code is &P##, prefix with item code.
  217. # - e.g. A&P1 -> A1A&P1
  218. if (substr($serial_data, 1, 1) eq '&') {
  219. $serial_data = $self->{x10_id} . $serial_data;
  220. }
  221. # Make sure that +-\d codes have the house_unit code prefixed
  222. # - e.g. device +12 -> A1A+12
  223. # Also round of to the nearest 5
  224. if ($serial_data =~ /^X?[\+\-]?\d+$/) {
  225. $serial_data = $self->{x10_id} . substr($self->{x10_id}, 1, 1) . $serial_data;
  226. }
  227. &main::print_log("X10: Outgoing data=$serial_data") if $main::config_parms{x10_errata} >= 4;
  228. # Allow for long strings like this: XAGAGAGAG (e.g. SmartLinc control)
  229. # - break it into individual codes (XAG XAG XAG)
  230. $serial_data =~ s/^X//;
  231. my $serial_chunk;
  232. while ($serial_data) {
  233. if ($serial_data =~ /^([A-P]STATUS)(\S*)/ or
  234. $serial_data =~ /^([A-P]PRESET_DIM1)(\S*)/ or
  235. $serial_data =~ /^([A-P]PRESET_DIM2)(\S*)/ or
  236. $serial_data =~ /^([A-P][1][0-6])(\S*)/ or
  237. $serial_data =~ /^([A-P][1-9A-W])(\S*)/ or
  238. $serial_data =~ /^([A-P]\&P\d+)(\S*)/ or # Pre Dim Cmds
  239. $serial_data =~ /^([A-P]Z\S*)/ or # Scene Cmds for Switchlinc
  240. $serial_data =~ /^([A-P]\d+\%)(\S*)/ or
  241. $serial_data =~ /^([A-P][\+\-]?\d+)(\S*)/) {
  242. $serial_chunk = $1;
  243. $serial_data = $2;
  244. # Allow for unit=9,10,11..16, instead of 9,A,B,C..F
  245. $serial_chunk = $1 . substr 'ABCDEFG', $2, 1 if $serial_chunk =~ /^(\S)1(\d)$/;
  246. &send_x10_data($interface, 'X' . $serial_chunk);
  247. }
  248. else {
  249. print "Serial_Item error, X10 string not parsed: $serial_data.\n";
  250. return;
  251. }
  252. }
  253. return;
  254. }
  255. else {
  256. $port_name = $interface if !$port_name;
  257. $port_name = 'Homevision' if !$port_name and $main::Serial_Ports{Homevision}{object}; #Since it's multifunction, it should be default
  258. $port_name = 'weeder' if !$port_name and $main::Serial_Ports{weeder}{object};
  259. $port_name = 'serial1' if !$port_name and $main::Serial_Ports{serial1}{object};
  260. $port_name = 'serial2' if !$port_name and $main::Serial_Ports{serial2}{object};
  261. unless ($port_name) {
  262. print "Error, serial set called, but no serial port found: data=$serial_data\n";
  263. return;
  264. }
  265. &send_serial_data($port_name, $serial_data);
  266. }
  267. # Check for X10 All-on All-off house codes
  268. # - If found, set states of all X10_Items on that housecode
  269. if ($serial_data =~ /^X(\S)([OP])$/) {
  270. print "db l=$main::Loop_Count X10: mh set House code $1 set to $2\n" if $main::Debug{x10};
  271. my $state = ($2 eq 'O') ? 'on' : 'off';
  272. &X10_Item::set_by_housecode($1, $state);
  273. }
  274. # Check for other items with the same codes
  275. # - If found, set them to the same state
  276. if ($serial_items_by_id{$serial_id} and my @refs = @{$serial_items_by_id{$serial_id}}) {
  277. for my $ref (@refs) {
  278. next if $ref eq $self;
  279. # Only compare between items on the same port
  280. my $port_name1 = ($self->{port_name} or ' ');
  281. my $port_name2 = ($ref ->{port_name} or ' ');
  282. next unless $port_name1 eq $port_name2;
  283. print "Serial_Item: Setting duplicate state: id=$serial_id item1=$$self{object_name} item2=$$ref{object_name}\n"
  284. if $main::Debug{serial};
  285. if ($state = $$ref{state_by_id}{$serial_id}) {
  286. $ref->set_receive($state, $set_by);
  287. }
  288. else {
  289. $ref->set_receive($serial_id, $set_by);
  290. }
  291. }
  292. }
  293. }
  294. # Avoid sending the same X10 code on consecutive passes.
  295. # It is pretty easy to create a loop with
  296. # tied items, groups, house codes, etc. Just ask Bill S. :)
  297. sub set_prev_pass_check {
  298. my ($self, $state);
  299. # print "db state=$state, sp=$self->{state_prev}, loop=$main::Loop_Count, lcp==$self->{change_pass}\n";
  300. if ($state =~ /^X/ and $self->{state_prev} and $state eq $self->{state_prev} and
  301. $self->{change_pass} >= ($main::Loop_Count - 1)) {
  302. my $item_name = $self->{object_name};
  303. print "X10 item set skipped on consecutive pass. item=$item_name state=$state id=$state\n";
  304. return 1;
  305. }
  306. return 0;
  307. }
  308. sub send_serial_data {
  309. my ($port_name, $serial_data) = @_;
  310. return if &main::proxy_send($port_name, 'send_serial_data', $serial_data);
  311. # The ncpuxa code works on a socket, not a serial port
  312. # but may be called as a Serial_Item
  313. unless ($main::Serial_Ports{$port_name}{object} or lc $port_name eq 'ncpuxa') {
  314. print "Error, serial port for $port_name has not been set: data=$serial_data\n";
  315. return;
  316. }
  317. if (lc $port_name eq 'homevision') {
  318. print "Using homevision to send: $serial_data\n";
  319. &Homevision::send($main::Serial_Ports{Homevision}{object}, $serial_data);
  320. }
  321. elsif (lc $port_name eq 'ncpuxa') {
  322. &main::print_log("Using ncpuxa to send: $serial_data");
  323. &ncpuxa_mh::send($main::config_parms{ncpuxa_port}, $serial_data);
  324. }
  325. else {
  326. my $datatype = $main::Serial_Ports{$port_name}{datatype};
  327. my $prefix = $main::Serial_Ports{$port_name}{prefix};
  328. $serial_data = $prefix . $serial_data if $prefix and $prefix ne '';
  329. $serial_data .= "\r" unless $datatype and $datatype eq 'raw';
  330. my $results = $main::Serial_Ports{$port_name}{object}->write($serial_data);
  331. # &main::print_log("serial port=$port_name out=$serial_data results=$results") if $main::Debug{serial};
  332. print "serial port=$port_name out=$serial_data results=$results\n" if $main::Debug{serial};
  333. }
  334. }
  335. my $x10_save_unit;
  336. sub send_x10_data {
  337. my ($interface, $serial_data) = @_;
  338. my ($isfunc);
  339. # Use proxy mh if present (avoids mh pauses for slow X10 xmits)
  340. return if &main::proxy_send($interface, 'send_x10_data', $serial_data);
  341. if ($serial_data =~ /^X[A-P][1-9A-G]$/) {
  342. $isfunc = 0;
  343. $x10_save_unit = $serial_data;
  344. }
  345. else {
  346. $isfunc = 1;
  347. }
  348. print "X10: interface=$interface isfunc=$isfunc save_unit=$x10_save_unit data=$serial_data\n" if $main::Debug{x10};
  349. if ($interface eq 'cm11') {
  350. # cm11 wants individual codes without X
  351. &ControlX10::CM11::send($main::Serial_Ports{cm11}{object},
  352. substr($serial_data, 1));
  353. }
  354. elsif ($interface eq 'bx24') {
  355. &X10_BX24::SendX10($serial_data);
  356. }
  357. elsif ($interface eq 'lynx10plc')
  358. {
  359. # marrick PLC wants XA1AK
  360. &Lynx10PLC::send_plc($main::Serial_Ports{Lynx10PLC}{object},
  361. "X" . substr($x10_save_unit, 1) .
  362. substr($serial_data, 1)) if $isfunc;
  363. }
  364. elsif ($interface eq 'cm17') {
  365. # cm17 wants A1K, not XA1AK
  366. &ControlX10::CM17::send($main::Serial_Ports{cm17}{object},
  367. substr($x10_save_unit, 1) . substr($serial_data, 2)) if $isfunc;
  368. }
  369. elsif ($interface eq 'homevision') {
  370. # homevision wants XA1AK
  371. if ($isfunc) {
  372. print "Using homevision to send: " .
  373. $x10_save_unit . substr($serial_data, 1) . "\n";
  374. &Homevision::send($main::Serial_Ports{Homevision}{object},
  375. $x10_save_unit . substr($serial_data, 1));
  376. }
  377. }
  378. elsif ($interface eq 'homebase') {
  379. # homebase wants individual codes without X
  380. print "Using homebase to send: $serial_data\n";
  381. &HomeBase::send_X10($main::Serial_Ports{HomeBase}{object}, substr($serial_data, 1));
  382. }
  383. elsif ($interface eq 'stargate') {
  384. # Stargate wants individual codes without X
  385. print "Using stargate to send: $serial_data\n";
  386. &Stargate::send_X10($main::Serial_Ports{Stargate}{object}, substr($serial_data, 1));
  387. }
  388. elsif ($interface eq 'houselinc') {
  389. # houselinc wants XA1AK
  390. if ($isfunc) {
  391. print "Using houselinc to send: " .
  392. $x10_save_unit . substr($serial_data, 1) . "\n";
  393. &HouseLinc::send_X10($main::Serial_Ports{HouseLinc}{object},
  394. $x10_save_unit . substr($serial_data, 1));
  395. }
  396. }
  397. elsif ($interface eq 'marrick') {
  398. # marrick wants XA1AK
  399. if ($isfunc) {
  400. print "Using marrick to send: " .
  401. $x10_save_unit . substr($serial_data, 1) . "\n";
  402. &Marrick::send_X10($main::Serial_Ports{Marrick}{object},
  403. $x10_save_unit . substr($serial_data, 1));
  404. }
  405. }
  406. elsif ($interface eq 'ncpuxa') {
  407. # ncpuxa wants individual codes with X
  408. &main::print_log("Using ncpuxa to send: $serial_data");
  409. &ncpuxa_mh::send($main::config_parms{ncpuxa_port}, $serial_data);
  410. }
  411. elsif ($interface eq 'weeder') {
  412. # Weeder table does not match what we defined in CM11,CM17,X10_Items.pm
  413. # - Dim -> L, Bright -> M, AllOn -> I, AllOff -> H
  414. my ($device, $house, $command) = $serial_data =~ /^X(\S\S)(\S)(\S+)/;
  415. # Allow for +-xx%
  416. my $dim_amount = 3;
  417. if ($command =~ /[\+\-]\d+/) {
  418. $dim_amount = int(10 * abs($command) / 100); # about 10 levels to 100%
  419. $command = ($command > 0) ? 'L' : 'M';
  420. }
  421. if ($command eq 'M') {
  422. $command = 'L' . (($house . 'L') x $dim_amount);
  423. }
  424. elsif ($command eq 'L') {
  425. $command = 'M' . (($house . 'M') x $dim_amount);
  426. }
  427. elsif ($command eq 'O') {
  428. $command = 'I';
  429. }
  430. elsif ($command eq 'P') {
  431. $command = 'H';
  432. }
  433. $serial_data = 'X' . $device . $house . $command;
  434. $main::Serial_Ports{weeder}{object}->write($serial_data);
  435. # Give weeder a chance to do the previous command
  436. # Surely there must be a better way!
  437. select undef, undef, undef, 1.2;
  438. }
  439. else {
  440. print "\nError, X10 interface not found: interface=$interface, data=$serial_data\n";
  441. }
  442. &send_x10_data_hooks($serial_data); # Created by &add_hooks
  443. }
  444. sub set_interface {
  445. my ($self, $interface) = @_;
  446. # Set the default interface
  447. unless ($interface) {
  448. if ($main::config_parms{x10_interface}) {
  449. $interface = $main::config_parms{x10_interface};
  450. }
  451. elsif ($main::Serial_Ports{cm11}{object}) {
  452. $interface = 'cm11';
  453. }
  454. elsif ($main::Serial_Ports{BX24}{object}) {
  455. $interface = 'bx24';
  456. }
  457. elsif ($main::Serial_Ports{Homevision}{object}) {
  458. $interface = 'homevision';
  459. }
  460. elsif ($main::Serial_Ports{HomeBase}{object}) {
  461. $interface = 'homebase';
  462. }
  463. elsif ($main::Serial_Ports{Stargate}{object}) {
  464. $interface = 'stargate';
  465. }
  466. elsif ($main::Serial_Ports{HouseLinc}{object}) {
  467. $interface = 'houselinc';
  468. }
  469. elsif ($main::Serial_Ports{Marrick}{object}) {
  470. $interface = 'marrick';
  471. }
  472. elsif ($main::config_parms{ncpuxa_port}) {
  473. $interface = 'ncpuxa';
  474. }
  475. elsif ($main::Serial_Ports{cm17}{object}) {
  476. $interface = 'cm17';
  477. }
  478. elsif ($main::Serial_Ports{Lynx10PLC}{object}) {
  479. $interface = 'lynx10plc';
  480. }
  481. elsif ($main::Serial_Ports{weeder}{object}) {
  482. $interface = 'weeder';
  483. }
  484. }
  485. $$self{interface} = lc($interface) if $interface;
  486. }
  487. #
  488. # $Log$
  489. # Revision 1.68 2003/12/01 03:09:52 winter
  490. # - 2.85 release
  491. #
  492. # Revision 1.67 2003/11/23 20:26:01 winter
  493. # - 2.84 release
  494. #
  495. # Revision 1.66 2003/09/02 02:48:46 winter
  496. # - 2.83 release
  497. #
  498. # Revision 1.65 2003/07/06 17:55:11 winter
  499. # - 2.82 release
  500. #
  501. # Revision 1.64 2003/04/20 21:44:07 winter
  502. # - 2.80 release
  503. #
  504. # Revision 1.63 2003/03/09 19:34:41 winter
  505. # - 2.79 release
  506. #
  507. # Revision 1.62 2003/02/08 05:29:23 winter
  508. # - 2.78 release
  509. #
  510. # Revision 1.61 2003/01/12 20:39:20 winter
  511. # - 2.76 release
  512. #
  513. # Revision 1.60 2002/12/24 03:05:08 winter
  514. # - 2.75 release
  515. #
  516. # Revision 1.59 2002/10/13 02:07:59 winter
  517. # - 2.72 release
  518. #
  519. # Revision 1.58 2002/09/22 01:33:23 winter
  520. # - 2.71 release
  521. #
  522. # Revision 1.57 2002/07/01 22:25:28 winter
  523. # - 2.69 release
  524. #
  525. # Revision 1.56 2002/05/28 13:07:51 winter
  526. # - 2.68 release
  527. #
  528. # Revision 1.55 2002/03/31 18:50:39 winter
  529. # - 2.66 release
  530. #
  531. # Revision 1.54 2002/03/02 02:36:51 winter
  532. # - 2.65 release
  533. #
  534. # Revision 1.53 2002/01/19 21:11:12 winter
  535. # - 2.63 release
  536. #
  537. # Revision 1.52 2001/12/16 21:48:41 winter
  538. # - 2.62 release
  539. #
  540. # Revision 1.51 2001/10/21 01:22:32 winter
  541. # - 2.60 release
  542. #
  543. # Revision 1.50 2001/09/23 19:28:11 winter
  544. # - 2.59 release
  545. #
  546. # Revision 1.49 2001/08/12 04:02:58 winter
  547. # - 2.57 update
  548. #
  549. # Revision 1.48 2001/06/27 03:45:14 winter
  550. # - 2.54 release
  551. #
  552. # Revision 1.47 2001/04/15 16:17:21 winter
  553. # - 2.49 release
  554. #
  555. # Revision 1.46 2001/03/24 18:08:38 winter
  556. # - 2.47 release
  557. #
  558. # Revision 1.45 2001/02/04 20:31:31 winter
  559. # - 2.43 release
  560. #
  561. # Revision 1.44 2000/12/03 19:38:55 winter
  562. # - 2.36 release
  563. #
  564. # Revision 1.43 2000/11/12 21:02:38 winter
  565. # - 2.34 release
  566. #
  567. # Revision 1.42 2000/10/22 16:48:29 winter
  568. # - 2.32 release
  569. #
  570. # Revision 1.41 2000/10/01 23:29:40 winter
  571. # - 2.29 release
  572. #
  573. # Revision 1.40 2000/09/09 21:19:11 winter
  574. # - 2.28 release
  575. #
  576. # Revision 1.39 2000/08/19 01:22:36 winter
  577. # - 2.27 release
  578. #
  579. # Revision 1.38 2000/06/24 22:10:54 winter
  580. # - 2.22 release. Changes to read_table, tk_*, tie_* functions, and hook_ code
  581. #
  582. # Revision 1.37 2000/05/27 16:40:10 winter
  583. # - 2.20 release
  584. #
  585. # Revision 1.36 2000/05/06 16:34:32 winter
  586. # - 2.15 release
  587. #
  588. # Revision 1.35 2000/03/10 04:09:01 winter
  589. # - Add Ibutton support and more web changes
  590. #
  591. # Revision 1.34 2000/02/13 03:57:27 winter
  592. # - 2.00 release. New web server interface
  593. #
  594. # Revision 1.33 2000/02/12 06:11:37 winter
  595. # - commit lots of changes, in preperation for mh release 2.0
  596. #
  597. # Revision 1.32 2000/01/27 13:42:42 winter
  598. # - update version number
  599. #
  600. # Revision 1.31 2000/01/19 13:23:29 winter
  601. # - add yucky delay to Weeder X10 xmit
  602. #
  603. # Revision 1.30 2000/01/02 23:47:43 winter
  604. # - add Device:: to as Serilport check. Use 10, not 7, increments in weeder dim
  605. #
  606. # Revision 1.29 1999/12/09 03:00:21 winter
  607. # - added Weeder bright/dim support
  608. #
  609. # Revision 1.28 1999/11/08 02:16:17 winter
  610. # - Move X10 stuff to X10_Items.pm. Fix close method
  611. #
  612. # Revision 1.27 1999/11/02 14:51:36 winter
  613. # - delete port in any case in stop method
  614. #
  615. # Revision 1.26 1999/10/31 14:49:04 winter
  616. # - added X10 &P## preset dim option and X10_Lamp item
  617. #
  618. # Revision 1.25 1999/10/27 12:42:27 winter
  619. # - add delete to serial_ports_by_port in sub close
  620. #
  621. # Revision 1.24 1999/10/09 20:36:49 winter
  622. # - add call to set_interface in first new method. Change to ControlX10
  623. #
  624. # Revision 1.23 1999/10/02 22:41:10 winter
  625. # - move interface stuff to set_interface, so we can use for x10_appliances also
  626. #
  627. # Revision 1.22 1999/09/27 03:16:32 winter
  628. # - move cm11 to HomeAutomation dir
  629. #
  630. # Revision 1.21 1999/09/12 16:57:07 winter
  631. # - point to new cm17 path
  632. #
  633. # Revision 1.20 1999/08/30 00:23:30 winter
  634. # - add set_dtr set_rts. Add check on loop_count
  635. #
  636. # Revision 1.19 1999/08/02 02:24:21 winter
  637. # - Add STATUS state
  638. #
  639. # Revision 1.18 1999/06/27 20:12:09 winter
  640. # - add CM17 support
  641. #
  642. # Revision 1.17 1999/06/20 22:32:43 winter
  643. # - check for raw datatype on writes
  644. #
  645. # Revision 1.16 1999/04/29 12:25:20 winter
  646. # - add House all on/off states
  647. #
  648. # Revision 1.15 1999/03/21 17:35:36 winter
  649. # - add datatype raw
  650. #
  651. # Revision 1.14 1999/03/12 04:30:24 winter
  652. # - add start, stop, and set_receive methods
  653. #
  654. # Revision 1.13 1999/02/16 02:06:57 winter
  655. # - add homebase send errata
  656. #
  657. # Revision 1.12 1999/02/08 03:50:25 winter
  658. # - re-enable serial writes! Bug introduced in last install.
  659. #
  660. # Revision 1.11 1999/02/08 00:30:54 winter
  661. # - make serial port prints depend on debug parm
  662. #
  663. # Revision 1.10 1999/01/30 19:55:45 winter
  664. # - add more checks for blank objects, so we don't abend
  665. #
  666. # Revision 1.9 1999/01/23 16:23:43 winter
  667. # - change the Serial_Port object to match Socket_Port format
  668. #
  669. # Revision 1.8 1999/01/13 14:11:03 winter
  670. # - add some more debug records
  671. #
  672. # Revision 1.7 1999/01/07 01:55:40 winter
  673. # - add 5% increments on X10_Item
  674. #
  675. # Revision 1.6 1998/12/10 14:34:19 winter
  676. # - fix empty state case
  677. #
  678. # Revision 1.5 1998/12/07 14:33:27 winter
  679. # - add dim level support. Allow for arbitrary set commands.
  680. #
  681. # Revision 1.4 1998/11/15 22:04:26 winter
  682. # - add support for generic serial ports
  683. #
  684. # Revision 1.3 1998/09/12 22:13:14 winter
  685. # - added HomeBase call
  686. #
  687. # Revision 1.2 1998/08/29 20:46:36 winter
  688. # - allow for cm11 interface
  689. #
  690. #
  691. 1;