somfy_sdn 1.0.12 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/sdn/message.rb CHANGED
@@ -1,15 +1,21 @@
1
- require 'sdn/messages/helpers'
1
+ require 'sdn/message/helpers'
2
2
 
3
3
  module SDN
4
4
  class MalformedMessage < RuntimeError; end
5
5
 
6
6
  class Message
7
7
  class << self
8
- def expected_response
8
+ def inherited(klass)
9
+ return Message.inherited(klass) unless self == Message
10
+ @message_map = nil
11
+ (@subclasses ||= []) << klass
12
+ end
13
+
14
+ def expected_response?(message)
9
15
  if name =~ /::Get([A-Za-z]+)/
10
- const_get("Post#{$1}", true)
16
+ message.class.name == name.sub("::Get", "::Post")
11
17
  else
12
- Ack
18
+ message.is_a?(Ack) || message.is_a?(Nack)
13
19
  end
14
20
  end
15
21
 
@@ -19,20 +25,22 @@ module SDN
19
25
  # we loop here scanning for a valid message
20
26
  loop do
21
27
  offset += 1
28
+ # give these weird messages a chance
29
+ result = ILT2::MasterControl.parse(data)
30
+ return result if result
31
+
22
32
  return [nil, 0] if data.length - offset < 11
23
33
  msg = to_number(data[offset])
24
34
  length = to_number(data[offset + 1])
25
35
  ack_requested = length & 0x80 == 0x80
26
36
  length &= 0x7f
27
37
  # impossible message
28
- next if length < 11 || length > 32
38
+ next if length < 11 || length > 43
29
39
  # don't have enough data for what this message wants;
30
40
  # it could be garbage on the line so keep scanning
31
41
  next if length > data.length - offset
32
42
 
33
- message_class = constants.find { |c| (const_get(c, false).const_get(:MSG, false) rescue nil) == msg }
34
- message_class = const_get(message_class, false) if message_class
35
- message_class ||= UnknownMessage
43
+ message_class = message_map[msg] || UnknownMessage
36
44
 
37
45
  calculated_sum = checksum(data.slice(offset, length - 2))
38
46
  read_sum = data.slice(offset + length - 2, 2)
@@ -41,34 +49,41 @@ module SDN
41
49
  break
42
50
  end
43
51
 
44
- puts "discarding invalid data prior to message #{data[0...offset].map { |b| '%02x' % b }.join(' ')}" unless offset == 0
45
- puts "read #{data.slice(offset, length).map { |b| '%02x' % b }.join(' ')}"
46
-
47
- reserved = to_number(data[offset + 2])
52
+ node_type = node_type_from_number(to_number(data[offset + 2]))
48
53
  src = transform_param(data.slice(offset + 3, 3))
49
54
  dest = transform_param(data.slice(offset + 6, 3))
50
55
  begin
51
- result = message_class.new(reserved: reserved, ack_requested: ack_requested, src: src, dest: dest)
56
+ result = message_class.new(node_type: node_type, ack_requested: ack_requested, src: src, dest: dest)
52
57
  result.parse(data.slice(offset + 9, length - 11))
53
58
  result.msg = msg if message_class == UnknownMessage
54
59
  rescue ArgumentError => e
55
- puts "discarding illegal message #{e}"
60
+ SDN.logger.warn "discarding illegal message of type #{message_class.name}: #{e}"
56
61
  result = nil
57
62
  end
58
63
  [result, offset + length]
59
64
  end
65
+
66
+ private
67
+
68
+ def message_map
69
+ @message_map ||=
70
+ @subclasses.inject({}) do |memo, klass|
71
+ next memo unless klass.constants(false).include?(:MSG)
72
+ memo[klass.const_get(:MSG, false)] = klass
73
+ memo
74
+ end
75
+ end
60
76
  end
61
77
 
62
78
  include Helpers
63
79
  singleton_class.include Helpers
64
80
 
65
- attr_reader :reserved, :ack_requested, :src, :dest
66
- attr_writer :ack_requested
81
+ attr_accessor :node_type, :ack_requested, :src, :dest
67
82
 
68
- def initialize(reserved: nil, ack_requested: false, src: nil, dest: nil)
69
- @reserved = reserved || 0x02 # message sent to Sonesse 30
83
+ def initialize(node_type: nil, ack_requested: false, src: nil, dest: nil)
84
+ @node_type = node_type || 0
70
85
  @ack_requested = ack_requested
71
- if src.nil? && is_group_address?(dest)
86
+ if src.nil? && !dest.nil? && is_group_address?(dest)
72
87
  src = dest
73
88
  dest = nil
74
89
  end
@@ -81,7 +96,7 @@ module SDN
81
96
  end
82
97
 
83
98
  def serialize
84
- result = transform_param(reserved) + transform_param(src) + transform_param(dest) + params
99
+ result = transform_param(node_type_to_number(node_type)) + transform_param(src) + transform_param(dest) + params
85
100
  length = result.length + 4
86
101
  length |= 0x80 if ack_requested
87
102
  result = transform_param(self.class.const_get(:MSG)) + transform_param(length) + result
@@ -94,11 +109,11 @@ module SDN
94
109
  end
95
110
 
96
111
  def inspect
97
- "#<%s @reserved=%02xh, @ack_requested=%s, @src=%s, @dest=%s%s>" % [self.class.name, reserved, ack_requested, print_address(src), print_address(dest), class_inspect]
112
+ "#<%s @node_type=%s, @ack_requested=%s, @src=%s, @dest=%s%s>" % [self.class.name, node_type_to_string(node_type), ack_requested, print_address(src), print_address(dest), class_inspect]
98
113
  end
99
114
 
100
115
  def class_inspect
101
- ivars = instance_variables - [:@reserved, :@ack_requested, :@src, :@dest]
116
+ ivars = instance_variables - [:@node_type, :@ack_requested, :@src, :@dest, :@params]
102
117
  return if ivars.empty?
103
118
  ivars.map { |iv| ", #{iv}=#{instance_variable_get(iv).inspect}" }.join
104
119
  end
@@ -121,19 +136,36 @@ module SDN
121
136
  class Nack < Message
122
137
  MSG = 0x6f
123
138
  PARAMS_LENGTH = 1
124
- VALUES = { data_error: 0x01, unknown_message: 0x10, node_is_locked: 0x20, wrong_position: 0x21, limits_not_set: 0x22, ip_not_set: 0x23, out_of_range: 0x24, busy: 0xff }
139
+ VALUES = { data_error: 0x01,
140
+ unknown_message: 0x10,
141
+ node_is_locked: 0x20,
142
+ wrong_position: 0x21,
143
+ limits_not_set: 0x22,
144
+ ip_not_set: 0x23,
145
+ out_of_range: 0x24,
146
+ busy: 0xff }
147
+ # 17 limits not set?
148
+ # 37 not implemented? (get motor rolling speed)
149
+ # 39 at limit? blocked?
150
+
125
151
 
126
152
  # presumed
127
153
  attr_accessor :error_code
128
154
 
155
+ def initialize(dest = nil, error_code = nil, **kwargs)
156
+ kwargs[:dest] ||= dest
157
+ super(**kwargs)
158
+ self.error_code = error_code
159
+ end
160
+
129
161
  def parse(params)
130
162
  super
131
163
  error_code = to_number(params[0])
132
- self.error_code = VALUES[error_code] || error_code
164
+ self.error_code = VALUES.invert[error_code] || error_code
133
165
  end
134
166
  end
135
167
 
136
- class Ack < Message
168
+ class Ack < SimpleRequest
137
169
  MSG = 0x7f
138
170
  PARAMS_LENGTH = 0
139
171
  end
@@ -142,10 +174,25 @@ module SDN
142
174
  class UnknownMessage < Message
143
175
  attr_accessor :msg, :params
144
176
 
177
+ def initialize(params = [], **kwargs)
178
+ super(**kwargs)
179
+ self.params = params
180
+ end
181
+
145
182
  alias parse params=
146
183
 
184
+ def serialize
185
+ # prevent serializing something we don't know
186
+ raise NotImplementedError unless params
187
+ super
188
+ end
189
+
147
190
  def class_inspect
148
- result = ", @msg=%02xh" % msg
191
+ result = if self.class == UnknownMessage
192
+ result = ", @msg=%02xh" % msg
193
+ else
194
+ super || ""
195
+ end
149
196
  return result if params.empty?
150
197
 
151
198
  result << ", @params=#{params.map { |b| "%02x" % b }.join(' ')}"
@@ -154,7 +201,11 @@ module SDN
154
201
  end
155
202
  end
156
203
 
157
- require 'sdn/messages/control'
158
- require 'sdn/messages/get'
159
- require 'sdn/messages/post'
160
- require 'sdn/messages/set'
204
+ require 'sdn/message/control'
205
+ require 'sdn/message/get'
206
+ require 'sdn/message/post'
207
+ require 'sdn/message/set'
208
+ require 'sdn/message/ilt2/get'
209
+ require 'sdn/message/ilt2/master_control'
210
+ require 'sdn/message/ilt2/post'
211
+ require 'sdn/message/ilt2/set'
@@ -1,5 +1,46 @@
1
1
  module SDN
2
2
  class Message
3
+ class Lock < Message
4
+ MSG = 0x06
5
+ PARAMS_LENGTH = 5
6
+ TARGET_TYPE = { current: 0, up_limit: 1, down_limit: 2, ip: 4, unlock: 5, position_percent: 7 }
7
+
8
+ attr_reader :target_type, :target, :priority
9
+
10
+ def initialize(dest = nil, target_type = :unlock, target = nil, priority = 1, **kwargs)
11
+ kwargs[:dest] ||= dest
12
+ super(**kwargs)
13
+ self.target_type = target_type
14
+ self.target = target
15
+ self.priority = priority
16
+ end
17
+
18
+ def target_type=(value)
19
+ raise ArgumentError, "target_type must be one of :current, :up_limit, :down_limit, :ip, :unlock, or :position_percent" unless TARGET_TYPE.keys.include?(value)
20
+ @target_type = value
21
+ end
22
+
23
+ def target=(value)
24
+ @target = value&. & 0xffff
25
+ end
26
+
27
+ def priority=(value)
28
+ @priority = value & 0xff
29
+ end
30
+
31
+ def parse(params)
32
+ super
33
+ self.target_type = TARGET_TYPE.invert[to_number(params[0])]
34
+ target = to_number(params[1..2], nillable: true)
35
+ self.target = target
36
+ self.priority = to_number(params[3])
37
+ end
38
+
39
+ def params
40
+ transform_param(TARGET_TYPE[target_type]) + from_number(target, 2) + transform_param(priority) + transform_param(0)
41
+ end
42
+ end
43
+
3
44
  # Move in momentary mode
4
45
  class Move < Message
5
46
  MSG = 0x01
@@ -32,7 +73,7 @@ module SDN
32
73
  end
33
74
 
34
75
  def duration=(value)
35
- raise ArgumentError, "duration must be in range 0x0a to 0xff (#{value})" unless value && value >= 0x0a && value <= 0xff
76
+ raise ArgumentError, "duration must be in range 0x0a to 0xff (#{value})" if value && (value < 0x0a || value > 0xff)
36
77
  @duration = value
37
78
  end
38
79
 
@@ -48,18 +89,43 @@ module SDN
48
89
  end
49
90
  end
50
91
 
51
- # Stop movement
52
- class Stop < Message
53
- MSG = 0x02
54
- PARAMS_LENGTH = 1
92
+ # Move relative to current position
93
+ class MoveOf < Message
94
+ MSG = 0x04
95
+ PARAMS_LENGTH = 4
96
+ TARGET_TYPE = { next_ip: 0x00, previous_ip: 0x01, jog_down_pulses: 0x02, jog_up_pulses: 0x03, jog_down_ms: 0x04, jog_up_ms: 0x05 }
55
97
 
56
- def initialize(dest = nil, **kwargs)
98
+ attr_reader :target_type, :target
99
+
100
+ def initialize(dest = nil, target_type = nil, target = nil, **kwargs)
57
101
  kwargs[:dest] ||= dest
58
102
  super(**kwargs)
103
+ self.target_type = target_type
104
+ self.target = target
105
+ end
106
+
107
+ def parse(params)
108
+ super
109
+ self.target_type = TARGET_TYPE.invert[to_number(params[0])]
110
+ target = to_number(params[1..2], nillable: true)
111
+ target *= 10 if %I{jog_down_ms jog_up_ms}.include?(target_type)
112
+ self.target = target
113
+ end
114
+
115
+ def target_type=(value)
116
+ raise ArgumentError, "target_type must be one of :next_ip, :previous_ip, :jog_down_pulses, :jog_up_pulses, :jog_down_ms, :jog_up_ms" unless value.nil? || TARGET_TYPE.keys.include?(value)
117
+ @target_type = value
118
+ end
119
+
120
+ def target=(value)
121
+ value &= 0xffff if value
122
+ @target = value
59
123
  end
60
124
 
61
125
  def params
62
- transform_param(0)
126
+ param = target || 0xffff
127
+ param /= 10 if %I{jog_down_ms jog_up_ms}.include?(target_type)
128
+ transform_param(TARGET_TYPE[target_type]) + from_number(param, 2) + transform_param(0)
63
129
  end
64
130
  end
65
131
 
@@ -107,43 +173,18 @@ module SDN
107
173
  end
108
174
  end
109
175
 
110
- # Move relative to current position
111
- class MoveOf < Message
112
- MSG = 0x04
113
- PARAMS_LENGTH = 4
114
- TARGET_TYPE = { next_ip: 0x00, previous_ip: 0x01, jog_down_pulses: 0x02, jog_up_pulses: 0x03, jog_down_ms: 0x04, jog_up_ms: 0x05 }
115
-
116
- attr_reader :target_type, :target
176
+ # Stop movement
177
+ class Stop < Message
178
+ MSG = 0x02
179
+ PARAMS_LENGTH = 1
117
180
 
118
- def initialize(dest = nil, target_type = nil, target = nil, **kwargs)
181
+ def initialize(dest = nil, **kwargs)
119
182
  kwargs[:dest] ||= dest
120
183
  super(**kwargs)
121
- self.target_type = target_type
122
- self.target = target
123
- end
124
-
125
- def parse(params)
126
- super
127
- self.target_type = TARGET_TYPE.invert[to_number(params[0])]
128
- target = to_number(params[1..2], nillable: true)
129
- target *= 10 if %I{jog_down_ms jog_up_ms}.include?(target_type)
130
- self.target = target
131
- end
132
-
133
- def target_type=(value)
134
- raise ArgumentError, "target_type must be one of :next_ip, :previous_ip, :jog_down_pulses, :jog_up_pulses, :jog_down_ms, :jog_up_ms" unless value.nil? || TARGET_TYPE.keys.include?(value)
135
- @target_type = value
136
- end
137
-
138
- def target=(value)
139
- value &= 0xffff if value
140
- @target = value
141
184
  end
142
185
 
143
186
  def params
144
- param = target || 0xffff
145
- param /= 10 if %I{jog_down_ms jog_up_ms}.include?(target_type)
146
- transform_param(TARGET_TYPE[target_type]) + from_number(param, 2) + transform_param(0)
187
+ transform_param(0)
147
188
  end
148
189
  end
149
190
 
@@ -1,32 +1,43 @@
1
1
  module SDN
2
2
  class Message
3
- class GetMotorPosition < SimpleRequest
4
- MSG = 0x0c
5
- end
3
+ class GetGroupAddr < Message
4
+ MSG = 0x41
5
+ PARAMS_LENGTH = 1
6
6
 
7
- class GetMotorStatus < SimpleRequest
8
- MSG = 0x0e
9
- end
7
+ attr_reader :group_index
10
8
 
11
- class GetMotorLimits < SimpleRequest
12
- MSG = 0x21
9
+ def initialize(dest = nil, group_index = 1, **kwargs)
10
+ kwargs[:dest] ||= dest
11
+ super(**kwargs)
12
+ self.group_index = group_index
13
+ end
14
+
15
+ def parse(params)
16
+ super
17
+ self.group_index = to_number(params[0]) + 1
18
+ end
19
+
20
+ def group_index=(value)
21
+ raise ArgumentError, "group_index is out of range" unless (1..16).include?(value)
22
+ @group_index = value
23
+ end
24
+
25
+ def params
26
+ transform_param(group_index - 1)
27
+ end
13
28
  end
14
29
 
15
30
  class GetMotorDirection < SimpleRequest
16
31
  MSG = 0x22
17
32
  end
18
33
 
19
- class GetMotorRollingSpeed < SimpleRequest
20
- MSG = 0x23
21
- end
22
-
23
34
  class GetMotorIP < Message
24
35
  MSG = 0x25
25
36
  PARAMS_LENGTH = 1
26
37
 
27
38
  attr_reader :ip
28
39
 
29
- def initialize(dest = nil, ip = nil, **kwargs)
40
+ def initialize(dest = nil, ip = 1, **kwargs)
30
41
  kwargs[:dest] ||= dest
31
42
  super(**kwargs)
32
43
  self.ip = ip
@@ -34,48 +45,37 @@ module SDN
34
45
 
35
46
  def parse(params)
36
47
  super
37
- self.ip = to_number(params[0], nillable: true)
48
+ self.ip = to_number(params[0])
38
49
  end
39
50
 
40
51
  def ip=(value)
41
- raise ArgumentError, "invalid IP #{ip} (should be 1-16)" unless ip.nil? || (1..16).include?(ip)
52
+ raise ArgumentError, "invalid IP #{value} (should be 1-16)" unless (1..16).include?(value)
42
53
  @ip = value
43
54
  end
44
55
 
45
56
  def params
46
- transform_param(@ip || 0xff)
57
+ transform_param(ip)
47
58
  end
48
59
  end
49
60
 
50
- class GetGroupAddr < Message
51
- MSG = 0x41
52
- PARAMS_LENGTH = 1
53
-
54
- attr_reader :group_index
55
-
56
- def initialize(dest = nil, group_index = 0, **kwargs)
57
- kwargs[:dest] ||= dest
58
- super(**kwargs)
59
- self.group_index = group_index
60
- end
61
+ class GetMotorLimits < SimpleRequest
62
+ MSG = 0x21
63
+ end
61
64
 
62
- def parse(params)
63
- super
64
- self.group_index = to_number(params[0])
65
- end
65
+ class GetMotorPosition < SimpleRequest
66
+ MSG = 0x0c
67
+ end
66
68
 
67
- def group_index=(value)
68
- raise ArgumentError, "group_index is out of range" unless (0...16).include?(value)
69
- @group_index = value
70
- end
69
+ class GetMotorRollingSpeed < SimpleRequest
70
+ MSG = 0x23
71
+ end
71
72
 
72
- def params
73
- transform_param(group_index)
74
- end
73
+ class GetMotorStatus < SimpleRequest
74
+ MSG = 0x0e
75
75
  end
76
76
 
77
- class GetNodeLabel < SimpleRequest
78
- MSG = 0x45
77
+ class GetNetworkLock < SimpleRequest
78
+ MSG = 0x26
79
79
  end
80
80
 
81
81
  class GetNodeAddr < Message
@@ -88,6 +88,14 @@ module SDN
88
88
  end
89
89
  end
90
90
 
91
+ class GetNodeAppVersion < SimpleRequest
92
+ MSG = 0x74
93
+ end
94
+
95
+ class GetNodeLabel < SimpleRequest
96
+ MSG = 0x45
97
+ end
98
+
91
99
  class GetNodeSerialNumber < SimpleRequest
92
100
  MSG = 0x4c
93
101
  end