File Coverage

blib/lib/X10/Home.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             ###########################################
2             package X10::Home;
3             ###########################################
4 3     3   83164 use strict;
  3         7  
  3         99  
5 3     3   16 use warnings;
  3         8  
  3         91  
6 3     3   2812 use YAML qw(LoadFile);
  3         37102  
  3         202  
7 3     3   3285 use Log::Log4perl qw(:easy);
  3         124110  
  3         24  
8 3     3   6244 use Device::SerialPort;
  3         118749  
  3         204  
9 3     3   35 use Fcntl qw/:flock/;
  3         7  
  3         453  
10 3     3   5260 use DB_File;
  0            
  0            
11              
12             our $VERSION = "0.04";
13              
14             my @CONF_PATHS = (
15             glob("~/.x10.conf"),
16             "/etc/x10.conf",
17             );
18              
19             my($STATUS_FILE) = glob("~/.x10.status");
20              
21             ###########################################
22             sub new {
23             ###########################################
24             my($class, %options) = @_;
25              
26             my $self = {
27             conf_paths => \@CONF_PATHS,
28             conf_file => undef,
29             commands => {
30             on => "J",
31             off => "K",
32             status => undef,
33             },
34             lockfile => '/tmp/x10_home.lock',
35             db_file => "/tmp/x10.status",
36             db_perm => 0666,
37             probe => 1,
38             %options,
39             };
40              
41             bless $self, $class;
42              
43             $self->init();
44              
45             return $self;
46             }
47              
48             ###########################################
49             sub init {
50             ###########################################
51             my($self) = @_;
52              
53             if(defined $self->{conf_file}) {
54             $self->{conf} = LoadFile( $self->{conf_file} );
55             } else {
56             for my $path (@{ $self->{conf_paths} }) {
57             if(-f $path) {
58             $self->{conf} = LoadFile( $path );
59             last;
60             }
61             }
62             }
63              
64             LOGDIE "No configuration file found (searched ",
65             join (", ", @{ $self->{conf_paths} }),
66             ")" unless defined $self->{conf};
67              
68             if(ref( $self->{conf} ) ne "HASH") {
69             LOGDIE "Configuration file invalid (not a hash)";
70             }
71            
72             $self->{conf}->{device} ||= "/dev/ttyS0";
73             $self->{conf}->{module} ||= "ControlX10::CM11";
74             $self->{conf}->{baudrate} ||= 4800;
75              
76             eval "require $self->{conf}->{module}";
77              
78             if($self->{probe}) {
79             $self->{serial} = Device::SerialPort->new(
80             $self->{conf}->{device}, undef);
81              
82             $self->{serial}->baudrate($self->{conf}->{baudrate});
83             }
84              
85             $self->{receivers} = {
86             map { $_->{name} => $_ } @{$self->{conf}->{receivers}} };
87              
88             $self->db_init() if defined $self->{db_file};
89              
90             1;
91             }
92              
93             ###########################################
94             sub db_init {
95             ###########################################
96             my($self) = @_;
97              
98             $self->{dbm} = {};
99              
100             dbmopen(%{$self->{dbm}},
101             $self->{db_file}, 0666) or
102             LOGDIE "Cannot open $self->{db_file}";
103              
104             chmod $self->{db_perm}, $self->{db_file};
105              
106             for (keys %{$self->{receivers}}) {
107             my $receiver = $self->{receivers}->{$_};
108             $self->{dbm}->{ $receiver->{name} } ||= "off";
109             }
110              
111             1;
112             }
113              
114             ###########################################
115             sub db_status {
116             ###########################################
117             my($self, $field, $value) = @_;
118              
119             if(defined $value) {
120             $self->{dbm}->{ $field } = $value;
121             }
122              
123             return $self->{dbm}->{ $field };
124             }
125              
126             ###########################################
127             sub send {
128             ###########################################
129             my($self, $receiver, $cmd) = @_;
130              
131             if(! exists $self->{receivers}->{$receiver}) {
132             ERROR "Unknown receiver '$receiver'";
133             return undef;
134             }
135              
136             if(! exists $self->{commands}->{$cmd}) {
137             ERROR "Unknown command '$cmd'";
138             return undef;
139             }
140              
141             my($house_code, $unit_code) = split //,
142             $self->{receivers}->{$receiver}->{code}, 2;
143              
144             my $send = "$self->{conf}->{module}" . "::" . "send";
145              
146             $self->lock();
147              
148             {
149             no strict 'refs';
150              
151             DEBUG "Addressing HC=$house_code UC=$unit_code";
152             $send->($self->{serial}, $house_code . $unit_code);
153            
154             DEBUG "Sending command $cmd $self->{commands}->{$cmd}";
155             $send->($self->{serial},
156             $house_code .
157             $self->{commands}->{$cmd});
158             }
159              
160             if(defined $self->{db_file}) {
161             DEBUG "Setting db status of $receiver to $cmd";
162             $self->db_status($receiver, $cmd);
163             }
164              
165             $self->unlock();
166              
167             1;
168             }
169              
170             ###########################################
171             sub lock {
172             ###########################################
173             my($self) = @_;
174              
175             open my $fh, ">>$self->{lockfile}" or
176             LOGDIE "Cannot open lockfile $self->{lockfile} ($!)";
177             flock($fh, LOCK_EX);
178              
179             $self->{fh} = $fh;
180             }
181              
182             ###########################################
183             sub unlock {
184             ###########################################
185             my($self) = @_;
186              
187             if(! defined $self->{fh}) {
188             LOGDIE "Called unlock without previous lock";
189             }
190              
191             flock($self->{fh}, LOCK_UN);
192             close $self->{fh};
193             $self->{fh} = undef;
194             unlink $self->{lockfile};
195             }
196              
197             ###########################################
198             sub receivers {
199             ###########################################
200             my($self) = @_;
201             return keys %{$self->{receivers}};
202             }
203              
204             ###########################################
205             sub command_valid {
206             ###########################################
207             my($self, $command) = @_;
208              
209             return exists $self->{commands}->{$command};
210             }
211              
212             ###########################################
213             sub DESTROY {
214             ###########################################
215             my($self) = @_;
216              
217             dbmclose(%{$self->{dbm}});
218             }
219              
220             1;
221              
222             __END__