line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package ControlX10::CM11; |
2
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
# An X10 ActiveHome interface, used by Misterhouse ( http://misterhouse.net ) |
5
|
|
|
|
|
|
|
# |
6
|
|
|
|
|
|
|
# Uses the Windows or Posix SerialPort.pm functions by Bill Birthisel, |
7
|
|
|
|
|
|
|
# available on CPAN |
8
|
|
|
|
|
|
|
# |
9
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
10
|
1
|
|
|
1
|
|
4079
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
42
|
|
11
|
1
|
|
|
1
|
|
5
|
use vars qw($VERSION $DEBUG @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
6087
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
require Exporter; |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
@ISA = qw(Exporter); |
16
|
|
|
|
|
|
|
@EXPORT= qw( send_cm11 receive_cm11 read_cm11 dim_decode_cm11 ); |
17
|
|
|
|
|
|
|
@EXPORT_OK= qw(); |
18
|
|
|
|
|
|
|
%EXPORT_TAGS = (FUNC => [qw( send_cm11 receive_cm11 |
19
|
|
|
|
|
|
|
read_cm11 dim_decode_cm11 )]); |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
Exporter::export_ok_tags('FUNC'); |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
$EXPORT_TAGS{ALL} = \@EXPORT_OK; |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
#### Package variable declarations #### |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
($VERSION) = q$Revision: 2.09 $ =~ /: (\S+)/; # Note: cvs version reset when we moved to sourceforge |
28
|
|
|
|
|
|
|
$DEBUG = 0; |
29
|
|
|
|
|
|
|
my $Last_Dcode; |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
sub send_cm11 { |
32
|
78
|
50
|
|
78
|
0
|
221
|
return unless ( 2 == @_ ); |
33
|
78
|
|
|
|
|
145
|
return ControlX10::CM11::send ( @_ ); |
34
|
|
|
|
|
|
|
} |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
sub receive_cm11 { |
37
|
1
|
50
|
|
1
|
0
|
6
|
return unless ( 1 == @_ ); |
38
|
1
|
|
|
|
|
4
|
return ControlX10::CM11::receive_buffer ( shift ); |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
sub read_cm11 { |
42
|
1
|
50
|
|
1
|
0
|
72
|
return unless ( 2 == @_ ); |
43
|
1
|
|
|
|
|
6
|
return ControlX10::CM11::read ( @_ ); |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
sub dim_decode_cm11 { |
47
|
11
|
50
|
|
11
|
0
|
26
|
return unless ( 1 == @_ ); |
48
|
11
|
|
|
|
|
90
|
return ControlX10::CM11::dim_level_decode ( shift ); |
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
# These tables are used in sending data |
52
|
|
|
|
|
|
|
my %table_hcodes = qw(A 0110 B 1110 C 0010 D 1010 E 0001 F 1001 G 0101 H 1101 |
53
|
|
|
|
|
|
|
I 0111 J 1111 K 0011 L 1011 M 0000 N 1000 O 0100 P 1100); |
54
|
|
|
|
|
|
|
my %table_dcodes = qw(1 0110 2 1110 3 0010 4 1010 5 0001 6 1001 7 0101 8 1101 |
55
|
|
|
|
|
|
|
9 0111 10 1111 11 0011 12 1011 13 0000 14 1000 15 0100 16 1100 |
56
|
|
|
|
|
|
|
A 1111 B 0011 C 1011 D 0000 E 1000 F 0100 G 1100); |
57
|
|
|
|
|
|
|
my %table_fcodes = qw(J 0010 K 0011 M 0100 L 0101 O 0001 P 0000 |
58
|
|
|
|
|
|
|
ALL_OFF 0000 ALL_ON 0001 ON 0010 OFF 0011 DIM 0100 BRIGHT 0101 |
59
|
|
|
|
|
|
|
-10 0100 -20 0100 -30 0100 -40 0100 |
60
|
|
|
|
|
|
|
-15 0100 -25 0100 -35 0100 -45 0100 -5 0100 |
61
|
|
|
|
|
|
|
-50 0100 -60 0100 -70 0100 -80 0100 -90 0100 |
62
|
|
|
|
|
|
|
-55 0100 -65 0100 -75 0100 -85 0100 -95 0100 |
63
|
|
|
|
|
|
|
+10 0101 +20 0101 +30 0101 +40 0101 |
64
|
|
|
|
|
|
|
+15 0101 +25 0101 +35 0101 +45 0101 +5 0101 |
65
|
|
|
|
|
|
|
+50 0101 +60 0101 +70 0101 +80 0101 +90 0101 |
66
|
|
|
|
|
|
|
+55 0101 +65 0101 +75 0101 +85 0101 +95 0101 |
67
|
|
|
|
|
|
|
ALL_LIGHTS_OFF 0110 EXTENDED_CODE 0111 HAIL_REQUEST 1000 HAIL_ACK 1001 |
68
|
|
|
|
|
|
|
PRESET_DIM1 1010 PRESET_DIM2 1011 EXTENDED_DATA 1100 |
69
|
|
|
|
|
|
|
STATUS_ON 1101 STATUS_OFF 1110 STATUS 1111); |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# These tables are used in receiving data |
73
|
|
|
|
|
|
|
my %table_hcodes2 = qw(0110 A 1110 B 0010 C 1010 D 0001 E 1001 F 0101 G 1101 H |
74
|
|
|
|
|
|
|
0111 I 1111 J 0011 K 1011 L 0000 M 1000 N 0100 O 1100 P); |
75
|
|
|
|
|
|
|
my %table_dcodes2 = qw(0110 1 1110 2 0010 3 1010 4 0001 5 1001 6 0101 7 1101 8 |
76
|
|
|
|
|
|
|
0111 9 1111 A 0011 B 1011 C 0000 D 1000 E 0100 F 1100 G); |
77
|
|
|
|
|
|
|
my %table_fcodes2 = qw(0010 J 0011 K 0100 L 0101 M 0001 O 0000 P |
78
|
|
|
|
|
|
|
0111 Z 1010 PRESET_DIM1 1011 PRESET_DIM2 |
79
|
|
|
|
|
|
|
1101 STATUS_ON 1110 STATUS_OFF 1111 STATUS); |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
sub receive_buffer { |
83
|
1
|
|
|
1
|
1
|
1
|
my ($serial_port) = @_; |
84
|
|
|
|
|
|
|
|
85
|
1
|
50
|
|
|
|
5
|
if (exists $main::config_parms{debug}) { |
86
|
1
|
50
|
|
|
|
6
|
$DEBUG = ($main::config_parms{debug} eq 'X10') ? 1 : 0; |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
|
89
|
1
|
|
|
|
|
3
|
my $pc_ready = pack('C', 0xc3); |
90
|
1
|
50
|
|
|
|
4
|
print "Bad cm11 pc_ready transmition\n" unless 1 == $serial_port->write($pc_ready); |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
# Lets not wait for data (use no_block option), or we loop too long and mh slows way down |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
# let the 0xc3 ack take hold ... emperically derived ... 1/2 misses at 20 ms |
95
|
1
|
|
|
|
|
40234
|
select undef, undef, undef, 40 / 1000; |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
|
98
|
1
|
|
|
|
|
8
|
my $data; |
99
|
1
|
50
|
|
|
|
7
|
return undef unless $data = &read($serial_port, 1); |
100
|
|
|
|
|
|
|
# my $data = &read($serial_port); |
101
|
|
|
|
|
|
|
|
102
|
1
|
|
|
|
|
47
|
my @bytes = split //, $data; |
103
|
|
|
|
|
|
|
|
104
|
1
|
|
|
|
|
62
|
my $length = shift @bytes; |
105
|
1
|
|
|
|
|
3
|
my $mask = shift @bytes; |
106
|
|
|
|
|
|
|
|
107
|
1
|
|
|
|
|
4
|
$length = unpack('C', $length); |
108
|
1
|
|
|
|
|
3
|
$mask = unpack('B8', $mask); |
109
|
1
|
|
|
|
|
5
|
my $data_h = unpack('H*', $data); |
110
|
1
|
50
|
|
|
|
4
|
print "receive buffer length=$length, mask=$mask, data_h=$data_h.\n" if $DEBUG; |
111
|
|
|
|
|
|
|
|
112
|
1
|
|
|
|
|
2
|
my ($house, $function, $device, $i, $extended_count); |
113
|
|
|
|
|
|
|
|
114
|
1
|
|
|
|
|
2
|
undef $data; |
115
|
1
|
|
|
|
|
3
|
foreach my $byte (@bytes) { |
116
|
|
|
|
|
|
|
# Send extended data into MH as untranslated hex. |
117
|
4
|
50
|
|
|
|
10
|
if ($extended_count) { |
118
|
0
|
|
|
|
|
0
|
$data .= unpack('H*', $byte); |
119
|
0
|
|
|
|
|
0
|
--$extended_count; |
120
|
0
|
|
|
|
|
0
|
++$i; |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
else { |
123
|
4
|
|
|
|
|
9
|
my $bits = unpack('B8', $byte); |
124
|
4
|
|
|
|
|
7
|
my $house_bits = substr($bits, 0, 4); |
125
|
4
|
|
|
|
|
8
|
my $code_bits = substr($bits, 4, 4); |
126
|
4
|
50
|
|
|
|
11
|
print "CM11 error, not a valid house code: $house_bits\n" unless $house = $table_hcodes2{$house_bits}; |
127
|
4
|
100
|
|
|
|
14
|
if (substr($mask, -(++$i), 1)) { |
128
|
1
|
50
|
|
|
|
14
|
print "CM11 error, not a valid function code: $code_bits at byte $i value $bits\n" unless $function = $table_fcodes2{$code_bits}; |
129
|
|
|
|
|
|
|
# print "function=$house$function\n"; |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
# Add device code back in, since this is not included in status :( |
132
|
1
|
50
|
|
|
|
4
|
$function = $Last_Dcode . $function if $function =~ /^STATUS/; |
133
|
|
|
|
|
|
|
# Handle Vehicle Interface RF Receiver extended code - assume length of 3 for extended |
134
|
1
|
50
|
|
|
|
5
|
$extended_count = 3 if ($function eq 'Z'); |
135
|
|
|
|
|
|
|
## 2.08, but 'Z' not numeric ## |
136
|
|
|
|
|
|
|
## $extended_count = 3 if ($function == 'Z'); |
137
|
|
|
|
|
|
|
|
138
|
1
|
|
|
|
|
2
|
$data .= $house . $function; |
139
|
1
|
50
|
|
|
|
4
|
print "CM11 db: data=$data\n" if $DEBUG; |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
else { |
142
|
3
|
50
|
|
|
|
12
|
print "CM11 error, not a valid device code: $code_bits\n" unless $device = $table_dcodes2{$code_bits}; |
143
|
|
|
|
|
|
|
# print "device=$house$device\n"; |
144
|
3
|
|
|
|
|
9
|
$data .= $house . $device; |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
# print "byte=$byte, $bits\n"; |
148
|
|
|
|
|
|
|
} |
149
|
1
|
|
|
|
|
10
|
return $data; |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
sub format_data { |
153
|
80
|
|
|
80
|
0
|
94
|
my ($house_code) = @_; |
154
|
|
|
|
|
|
|
|
155
|
80
|
100
|
|
|
|
147
|
print "CM11 send data=$house_code\n" if $DEBUG; |
156
|
|
|
|
|
|
|
|
157
|
80
|
|
|
|
|
76
|
my ($house, $code, $house_bits, $header, $code_bits, $function, $dim_level); |
158
|
0
|
|
|
|
|
0
|
my ($extended, $extended_string, $extended_checksum); |
159
|
|
|
|
|
|
|
|
160
|
80
|
|
|
|
|
398
|
($house, $code) = $house_code =~ /(\S)(\S+)/; |
161
|
80
|
|
|
|
|
133
|
$house = uc($house); |
162
|
80
|
|
|
|
|
93
|
$code = uc($code); |
163
|
|
|
|
|
|
|
|
164
|
80
|
100
|
|
|
|
179
|
unless ($house_bits = $table_hcodes{$house}) { |
165
|
1
|
|
|
|
|
5
|
print "CM11 error, invalid house code: $house\n"; |
166
|
1
|
|
|
|
|
3
|
return; |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
# $code can be |
170
|
|
|
|
|
|
|
# 1-9,A-G for Device code |
171
|
|
|
|
|
|
|
# d_xyz. for Extended code xyz for device d |
172
|
|
|
|
|
|
|
# xyz for Function codes, including +-## for bright/dim |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
# Test for extended code |
175
|
|
|
|
|
|
|
# - format is D&P## where D is device code and ## is the preset dim level |
176
|
79
|
100
|
|
|
|
437
|
if (my($dcode, $extended_data) = $code =~ /(\S)&P(\d+)/) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
177
|
3
|
50
|
|
|
|
9
|
unless ($code_bits = $table_dcodes{$dcode}) { |
178
|
0
|
|
|
|
|
0
|
print "CM11 error, invalid device in extended code. device=$dcode code=$code\n"; |
179
|
0
|
|
|
|
|
0
|
return; |
180
|
|
|
|
|
|
|
} |
181
|
3
|
100
|
66
|
|
|
20
|
unless (($extended_data >= 0) && ($extended_data < 65)) { |
182
|
1
|
|
|
|
|
6
|
print "CM11 error, invalid extended code. code=$code\n"; |
183
|
1
|
|
|
|
|
2
|
return; |
184
|
|
|
|
|
|
|
} |
185
|
2
|
|
|
|
|
4
|
$code_bits = '0111'; # Extended code |
186
|
|
|
|
|
|
|
# print "db cb=$code_bits\n"; |
187
|
2
|
|
|
|
|
3
|
$function = '1'; # Extended transmitions are a function |
188
|
2
|
|
|
|
|
2
|
$extended = '1'; |
189
|
2
|
|
|
|
|
4
|
$dim_level = 0; # Dim level is not applicable to extended transmitions. |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
# Hard codeded preset for now ... |
192
|
|
|
|
|
|
|
|
193
|
2
|
|
|
|
|
3
|
my $extended_junk = '00000101'; # This is not documented, so I have no idea why it is needed! |
194
|
|
|
|
|
|
|
# Emperically derived by looking at ActiveHome errata. |
195
|
|
|
|
|
|
|
|
196
|
2
|
|
|
|
|
2
|
my $extended_code = '00110001'; # Type=3 => Control Modules Func=1 => Preset Receiver |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
# Convert from bit to string |
199
|
2
|
|
|
|
|
5
|
my $b3 = pack('B8', $extended_junk); |
200
|
2
|
|
|
|
|
5
|
my $b4 = pack('C1', $extended_data); |
201
|
2
|
|
|
|
|
4
|
my $b5 = pack('B8', $extended_code); |
202
|
2
|
|
|
|
|
4
|
$extended_string = $b3 . $b4 . $b5; |
203
|
2
|
|
|
|
|
4
|
my $b3c = unpack('C', $b3); |
204
|
2
|
|
|
|
|
9
|
my $b4c = unpack('C', $b4); |
205
|
2
|
|
|
|
|
3
|
my $b5c = unpack('C', $b5); |
206
|
2
|
|
|
|
|
3
|
$extended_checksum = $b3c + $b4c + $b5c; |
207
|
2
|
50
|
|
|
|
7
|
if ($DEBUG) { |
208
|
0
|
|
|
|
|
0
|
printf "CM11 ed=%d, b345=0x%0.2x,0x%0.2x,0x%0.2x ex=%s cs=0x%0.2x\n", |
209
|
|
|
|
|
|
|
$extended_data, $b3c, $b4c, $b5c, $extended_string, |
210
|
|
|
|
|
|
|
$extended_checksum; |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
} |
213
|
|
|
|
|
|
|
# Test for device code |
214
|
|
|
|
|
|
|
elsif ($code_bits = $table_dcodes{$code}) { |
215
|
18
|
|
|
|
|
19
|
$function = '0'; |
216
|
18
|
|
|
|
|
20
|
$extended = '0'; |
217
|
18
|
|
|
|
|
15
|
$dim_level = 0; |
218
|
18
|
|
|
|
|
21
|
$Last_Dcode = $code; # This is desperate :) |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
# Test for function code |
221
|
|
|
|
|
|
|
elsif ($code_bits = $table_fcodes{$code}) { |
222
|
48
|
|
|
|
|
62
|
$function = '1'; |
223
|
48
|
|
|
|
|
55
|
$extended = '0'; |
224
|
48
|
100
|
100
|
|
|
471
|
if ($code eq 'DIM' or $code eq 'M' or $code eq 'BRIGHT' or $code eq 'L') { |
|
|
100
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
225
|
5
|
|
|
|
|
8
|
$dim_level = 50; |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
elsif ($code =~ /^[+-]\d\d$/) { |
228
|
23
|
|
|
|
|
45
|
$dim_level = abs($code); |
229
|
|
|
|
|
|
|
} |
230
|
|
|
|
|
|
|
else { |
231
|
20
|
|
|
|
|
25
|
$dim_level = 0; |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
else { |
235
|
10
|
|
|
|
|
53
|
print "CM11 error, invalid cm11 x10 code: $code\n"; |
236
|
10
|
|
|
|
|
22
|
return; |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
|
239
|
68
|
|
|
|
|
130
|
my $dim = int($dim_level * 22 / 100); # 22 levels = 100% |
240
|
68
|
|
|
|
|
243
|
$header = substr(unpack('B8', pack('C', $dim)), 3); |
241
|
|
|
|
|
|
|
|
242
|
68
|
|
|
|
|
98
|
$header .= '1'; # Bit 2 is always set to a 1 to ensure synchronization |
243
|
68
|
|
|
|
|
87
|
$header .= $function; # 0 for address, 1 for function |
244
|
68
|
|
|
|
|
64
|
$header .= $extended; # 0 for standard, 1 for extended transmition |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
# Convert from bit to string |
247
|
68
|
|
|
|
|
114
|
my $b1 = pack('B8', $header); |
248
|
68
|
|
|
|
|
159
|
my $b2 = pack('B8', $house_bits . $code_bits); |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
# Calculate checksum |
251
|
68
|
|
|
|
|
114
|
my $b1d = unpack('C', $b1); |
252
|
68
|
|
|
|
|
98
|
my $b2d = unpack('C', $b2); |
253
|
68
|
|
|
|
|
104
|
my $checksum = ($b1d + $b2d) & 0xff; |
254
|
|
|
|
|
|
|
|
255
|
68
|
|
|
|
|
81
|
my $data = $b1 . $b2; |
256
|
|
|
|
|
|
|
|
257
|
68
|
100
|
|
|
|
278
|
if ($extended) { |
258
|
2
|
|
|
|
|
3
|
$data .= $extended_string; |
259
|
2
|
|
|
|
|
4
|
$checksum = ($checksum + $extended_checksum) & 0xff; |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
|
262
|
68
|
100
|
|
|
|
125
|
printf("CM11 dim=$dim header=$header hb=$house_bits cb=$code_bits " . |
263
|
|
|
|
|
|
|
"bd=0x%0.2x,0x%0.2x checksum=0x%0.2x\n", |
264
|
|
|
|
|
|
|
$b1d, $b2d, $checksum) if $DEBUG; |
265
|
|
|
|
|
|
|
|
266
|
68
|
|
|
|
|
222
|
return $data, $checksum; |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
} |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
sub send { |
271
|
80
|
|
|
80
|
1
|
122
|
my ($serial_port, $house_code) = @_; |
272
|
|
|
|
|
|
|
|
273
|
80
|
50
|
|
|
|
178
|
if (exists $main::config_parms{debug}) { |
274
|
80
|
100
|
|
|
|
371
|
$DEBUG = ($main::config_parms{debug} eq 'X10') ? 1 : 0; |
275
|
|
|
|
|
|
|
# &Win32::SerialPort::debug(1) if $DEBUG; |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
|
278
|
80
|
|
|
|
|
125
|
my ($data_snd, $checksum) = &format_data($house_code); |
279
|
80
|
100
|
|
|
|
189
|
return unless $data_snd; |
280
|
|
|
|
|
|
|
|
281
|
68
|
|
|
|
|
70
|
my $retry_cnt = 0; |
282
|
68
|
100
|
|
|
|
126
|
RETRY: |
283
|
|
|
|
|
|
|
print "CM11 send: ", unpack('H*', $data_snd), "\n" if $DEBUG; |
284
|
|
|
|
|
|
|
|
285
|
68
|
50
|
|
|
|
222
|
print "Bad cm11 data send transmition\n" unless length($data_snd) == $serial_port->write($data_snd); |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
# Note: Skip the power fail check, because we the |
288
|
|
|
|
|
|
|
# checksum might be the power fail flag (0xa5) |
289
|
68
|
|
|
|
|
1547
|
my $data_rcv = &read($serial_port, 0, 1); |
290
|
|
|
|
|
|
|
|
291
|
68
|
|
|
|
|
114
|
my $data_d = unpack('C', $data_rcv); |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
# Unrelated incoming data ... process and re-start |
294
|
|
|
|
|
|
|
# Note: Some checksums will be 0x5a or 0xa5 ... skip this test if so |
295
|
68
|
50
|
66
|
|
|
310
|
if (($data_d == 0x5a or $data_d == 0xa5) and !($checksum == 0x5a or $checksum == 0xa5)) { |
|
|
|
33
|
|
|
|
|
|
|
|
66
|
|
|
|
|
296
|
0
|
|
|
|
|
0
|
print "Data received while xmiting data ... will receive and retry\n"; |
297
|
0
|
|
|
|
|
0
|
&receive_buffer($serial_port); |
298
|
0
|
0
|
|
|
|
0
|
goto RETRY if $retry_cnt++ < 3; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
|
301
|
68
|
50
|
|
|
|
120
|
if ($checksum != $data_d) { |
302
|
0
|
|
|
|
|
0
|
print "Bad checksum in cm11 send: cs1=$checksum cs2=$data_d. Will retry\n"; |
303
|
0
|
0
|
|
|
|
0
|
goto RETRY if $retry_cnt++ < 3; |
304
|
|
|
|
|
|
|
} |
305
|
|
|
|
|
|
|
|
306
|
68
|
100
|
|
|
|
132
|
print "CM11 ack\n" if $DEBUG; |
307
|
68
|
|
|
|
|
83
|
my $pc_ok = pack('C', 0x00); |
308
|
68
|
50
|
|
|
|
150
|
print "Bad cm11 acknowledge send transmition\n" unless 1 == $serial_port->write($pc_ok); |
309
|
|
|
|
|
|
|
|
310
|
68
|
|
|
|
|
1086
|
$data_rcv = &read($serial_port); |
311
|
68
|
|
|
|
|
132
|
$data_d = unpack('C', $data_rcv); |
312
|
|
|
|
|
|
|
|
313
|
68
|
50
|
0
|
|
|
115
|
if ($data_d == 0x55) { |
|
|
0
|
|
|
|
|
|
314
|
68
|
100
|
|
|
|
119
|
print "CM11 done\n" if $DEBUG; |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
# Unrelated incoming data ... process |
317
|
|
|
|
|
|
|
elsif ($data_d == 0x5a or $data_d == 0xa5) { |
318
|
0
|
|
|
|
|
0
|
print "Data received while xmiting data ... receive and retry\n"; |
319
|
0
|
|
|
|
|
0
|
&receive_buffer($serial_port); |
320
|
0
|
0
|
|
|
|
0
|
goto RETRY if $retry_cnt++ < 3; |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
|
323
|
68
|
50
|
|
|
|
167
|
if (exists $main::config_parms{debug}) { |
324
|
|
|
|
|
|
|
# &Win32::SerialPort::debug(0) if $DEBUG; |
325
|
|
|
|
|
|
|
} |
326
|
|
|
|
|
|
|
|
327
|
68
|
|
|
|
|
319
|
return $data_d; |
328
|
|
|
|
|
|
|
} |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
sub read { |
331
|
138
|
|
|
138
|
1
|
210
|
my ($serial_port, $no_block, $no_power_fail_check) = @_; |
332
|
138
|
|
|
|
|
142
|
my $data; |
333
|
|
|
|
|
|
|
# Note ... for dim commands > 20, this will time out after 20*40=1 seconds |
334
|
|
|
|
|
|
|
# No harm done, but we would rather not wait :) |
335
|
138
|
100
|
|
|
|
228
|
my $tries = ($no_block) ? 1 : 20; |
336
|
|
|
|
|
|
|
|
337
|
138
|
50
|
|
|
|
298
|
if (exists $main::config_parms{debug}) { |
338
|
138
|
100
|
|
|
|
346
|
$DEBUG = ($main::config_parms{debug} eq 'X10') ? 1 : 0; |
339
|
|
|
|
|
|
|
} |
340
|
|
|
|
|
|
|
|
341
|
138
|
|
|
|
|
288
|
while ($tries--) { |
342
|
138
|
100
|
66
|
|
|
329
|
print "." if $DEBUG and !$no_block; |
343
|
138
|
50
|
|
|
|
413
|
if ($data = $serial_port->input) { |
344
|
138
|
|
|
|
|
2142
|
my $data_d = unpack('C', $data); |
345
|
|
|
|
|
|
|
# printf("rcv data=%s, %x.\n", $data, $data_d); |
346
|
|
|
|
|
|
|
# my $pc_ready = pack('C', 0xc3); |
347
|
|
|
|
|
|
|
# $serial_data = "$pc_ready"; |
348
|
|
|
|
|
|
|
# print "serial1 out=$serial_data results=", $serial_port->write($serial_data), ".\n" if $DEBUG; |
349
|
138
|
100
|
|
|
|
280
|
printf("\nCM11 data=%s hex=%0.2lx\n", $data_d, $data_d) if $DEBUG; |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
# If we received the power-fail string (0xa5), reset with a blank macro command |
352
|
|
|
|
|
|
|
# - Protocol.txt says to send macros string, but that did not work. |
353
|
138
|
50
|
66
|
|
|
342
|
if ($data_d == 165 and !$no_power_fail_check) { |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
#55 to 48 timer download header (0x9b) |
356
|
|
|
|
|
|
|
#47 to 40 Current time (seconds) |
357
|
|
|
|
|
|
|
#39 to 32 Current time (minutes ranging from 0 to 119) |
358
|
|
|
|
|
|
|
#31 to 23 Current time (hours/2, ranging from 0 to 11) |
359
|
|
|
|
|
|
|
#23 to 16 Current year day (bits 0 to 7) |
360
|
|
|
|
|
|
|
#15 Current year day (bit 8) |
361
|
|
|
|
|
|
|
#14 to 8 Day mask (SMTWTFS) |
362
|
|
|
|
|
|
|
#7 to 4 Monitored house code |
363
|
|
|
|
|
|
|
#3 Reserved |
364
|
|
|
|
|
|
|
#2 Battery timer clear flag |
365
|
|
|
|
|
|
|
#1 Monitored status clear flag |
366
|
|
|
|
|
|
|
#0 Timer purge flag |
367
|
|
|
|
|
|
|
|
368
|
0
|
|
|
|
|
0
|
my ($Second, $Minute, $Hour, $Mday, $Month, $Year, $Wday, $Yday) = localtime time; |
369
|
0
|
|
|
|
|
0
|
my $localtime = localtime time; |
370
|
0
|
|
|
|
|
0
|
$Wday = 2 ** (7 - $Wday); |
371
|
0
|
0
|
|
|
|
0
|
if ($Yday > 255) { |
372
|
0
|
|
|
|
|
0
|
$Yday -= 256; |
373
|
0
|
|
|
|
|
0
|
$Wday *= 2; |
374
|
|
|
|
|
|
|
} |
375
|
0
|
|
|
|
|
0
|
my $power_reset = pack('C7', 0x9b, |
376
|
|
|
|
|
|
|
$Second, |
377
|
|
|
|
|
|
|
$Minute, |
378
|
|
|
|
|
|
|
$Hour, |
379
|
|
|
|
|
|
|
$Yday, |
380
|
|
|
|
|
|
|
$Wday, |
381
|
|
|
|
|
|
|
0x03); # Not sure what is best here. x10d.c did this. |
382
|
|
|
|
|
|
|
|
383
|
0
|
|
|
|
|
0
|
print "\nCM11 power fail detected. Resetting the CM11 clock with:\n $localtime\n"; |
384
|
0
|
|
|
|
|
0
|
my $results = $serial_port->write($power_reset); |
385
|
0
|
|
|
|
|
0
|
select undef, undef, undef, 50 / 1000; |
386
|
0
|
|
|
|
|
0
|
my $checksum = $serial_port->input; # Receive, but ignore, checksum |
387
|
0
|
0
|
|
|
|
0
|
if ($DEBUG) { |
388
|
0
|
|
|
|
|
0
|
printf "\npower_reset: %s %s %s %s %s %s %s\n", |
389
|
|
|
|
|
|
|
unpack ('H2H2H2H2H2H2H2', $power_reset); |
390
|
0
|
|
|
|
|
0
|
print " sent $results bytes\n"; |
391
|
0
|
|
|
|
|
0
|
printf " checksum = %x\n", ord($checksum); |
392
|
|
|
|
|
|
|
} |
393
|
0
|
|
|
|
|
0
|
my $pc_ok = pack('C', 0x00); |
394
|
0
|
0
|
|
|
|
0
|
print "Bad cm11 checksum acknowledge\n" unless 1 == $serial_port->write($pc_ok); |
395
|
|
|
|
|
|
|
# undef $data; |
396
|
|
|
|
|
|
|
} |
397
|
|
|
|
|
|
|
|
398
|
138
|
|
|
|
|
334
|
return $data; |
399
|
|
|
|
|
|
|
} |
400
|
|
|
|
|
|
|
# If we do not do this, we may get endless error messages. |
401
|
|
|
|
|
|
|
else { |
402
|
0
|
|
|
|
|
0
|
$serial_port->reset_error; |
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
|
405
|
0
|
0
|
|
|
|
0
|
if ($tries) { |
406
|
0
|
|
|
|
|
0
|
select undef, undef, undef, 50 / 1000; |
407
|
|
|
|
|
|
|
} |
408
|
|
|
|
|
|
|
} |
409
|
|
|
|
|
|
|
|
410
|
0
|
0
|
0
|
|
|
0
|
print "No data received from cm11\n" if ($DEBUG and !$no_block); |
411
|
0
|
|
|
|
|
0
|
return undef; |
412
|
|
|
|
|
|
|
} |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
sub dim_level_decode { |
415
|
11
|
|
|
11
|
1
|
16
|
my ($code) = @_; |
416
|
|
|
|
|
|
|
|
417
|
11
|
|
|
|
|
148
|
my %table_hcodes = qw(A 0110 B 1110 C 0010 D 1010 E 0001 F 1001 G 0101 H 1101 |
418
|
|
|
|
|
|
|
I 0111 J 1111 K 0011 L 1011 M 0000 N 1000 O 0100 P 1100); |
419
|
11
|
|
|
|
|
89
|
my %table_dcodes = qw(1 0110 2 1110 3 0010 4 1010 5 0001 6 1001 7 0101 8 1101 |
420
|
|
|
|
|
|
|
9 0111 10 1111 11 0011 12 1011 13 0000 14 1000 15 0100 16 1100 |
421
|
|
|
|
|
|
|
A 1111 B 0011 C 1011 D 0000 E 1000 F 0100 G 1100); |
422
|
|
|
|
|
|
|
|
423
|
11
|
50
|
|
|
|
27
|
if (exists $main::config_parms{debug}) { |
424
|
11
|
50
|
|
|
|
25
|
$DEBUG = ($main::config_parms{debug} eq 'X10') ? 1 : 0; |
425
|
|
|
|
|
|
|
} |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
# Convert bit string to decimal |
428
|
11
|
|
|
|
|
27
|
my $level_b = $table_hcodes{substr($code, 0, 1)} . $table_dcodes{substr($code, 1, 1)}; |
429
|
11
|
|
|
|
|
36
|
my $level_d = unpack('C', pack('B8', $level_b)); |
430
|
|
|
|
|
|
|
# Varies from 36 to 201, by 11, then to 210 as a max. |
431
|
|
|
|
|
|
|
# 16 different values. Round to nearest 5%, max of 95. |
432
|
11
|
|
|
|
|
38
|
my $level_p = int(100 * $level_d / 211); # Do not allow 100% ... not a valid state? |
433
|
|
|
|
|
|
|
## print "CM11 debug1: levelb=$level_b level_p=$level_p\n" if $DEBUG; |
434
|
11
|
|
|
|
|
19
|
$level_p = $level_p - ($level_p % 5); |
435
|
11
|
50
|
|
|
|
21
|
print "CM11 debug: dim_code=$code leveld=$level_d level_p=$level_p\n" if $DEBUG; |
436
|
11
|
|
|
|
|
91
|
return $level_p; |
437
|
|
|
|
|
|
|
} |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
return 1; # for require |
440
|
|
|
|
|
|
|
__END__ |