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.
- checksums.yaml +4 -4
- data/bin/somfy_sdn +60 -0
- data/lib/sdn.rb +16 -1
- data/lib/sdn/cli/mqtt.rb +369 -0
- data/lib/sdn/cli/mqtt/group.rb +31 -0
- data/lib/sdn/cli/mqtt/motor.rb +103 -0
- data/lib/sdn/cli/mqtt/read.rb +154 -0
- data/lib/sdn/cli/mqtt/subscriptions.rb +156 -0
- data/lib/sdn/cli/mqtt/write.rb +93 -0
- data/lib/sdn/cli/provisioner.rb +234 -0
- data/lib/sdn/cli/simulator.rb +197 -0
- data/lib/sdn/client.rb +84 -0
- data/lib/sdn/message.rb +81 -30
- data/lib/sdn/{messages → message}/control.rb +79 -38
- data/lib/sdn/{messages → message}/get.rb +48 -40
- data/lib/sdn/{messages → message}/helpers.rb +33 -3
- data/lib/sdn/message/ilt2/get.rb +48 -0
- data/lib/sdn/message/ilt2/master_control.rb +35 -0
- data/lib/sdn/message/ilt2/post.rb +127 -0
- data/lib/sdn/message/ilt2/set.rb +192 -0
- data/lib/sdn/{messages → message}/post.rb +127 -61
- data/lib/sdn/{messages → message}/set.rb +99 -84
- data/lib/sdn/version.rb +1 -1
- metadata +53 -16
- data/bin/sdn_mqtt_bridge +0 -5
- data/lib/sdn/messages/ilt2/get.rb +0 -9
- data/lib/sdn/messages/ilt2/post.rb +0 -18
- data/lib/sdn/messages/ilt2/set.rb +0 -59
- data/lib/sdn/mqtt_bridge.rb +0 -711
data/lib/sdn/message.rb
CHANGED
@@ -1,15 +1,21 @@
|
|
1
|
-
require 'sdn/
|
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
|
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
|
-
|
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 >
|
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 =
|
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
|
-
|
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(
|
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
|
-
|
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
|
-
|
66
|
-
attr_writer :ack_requested
|
81
|
+
attr_accessor :node_type, :ack_requested, :src, :dest
|
67
82
|
|
68
|
-
def initialize(
|
69
|
-
@
|
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(
|
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 @
|
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 - [:@
|
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,
|
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 <
|
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 =
|
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/
|
158
|
-
require 'sdn/
|
159
|
-
require 'sdn/
|
160
|
-
require 'sdn/
|
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})"
|
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
|
-
#
|
52
|
-
class
|
53
|
-
MSG =
|
54
|
-
PARAMS_LENGTH =
|
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
|
-
|
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
|
-
|
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
|
-
#
|
111
|
-
class
|
112
|
-
MSG =
|
113
|
-
PARAMS_LENGTH =
|
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,
|
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
|
-
|
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
|
4
|
-
MSG =
|
5
|
-
|
3
|
+
class GetGroupAddr < Message
|
4
|
+
MSG = 0x41
|
5
|
+
PARAMS_LENGTH = 1
|
6
6
|
|
7
|
-
|
8
|
-
MSG = 0x0e
|
9
|
-
end
|
7
|
+
attr_reader :group_index
|
10
8
|
|
11
|
-
|
12
|
-
|
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 =
|
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]
|
48
|
+
self.ip = to_number(params[0])
|
38
49
|
end
|
39
50
|
|
40
51
|
def ip=(value)
|
41
|
-
raise ArgumentError, "invalid 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(
|
57
|
+
transform_param(ip)
|
47
58
|
end
|
48
59
|
end
|
49
60
|
|
50
|
-
class
|
51
|
-
MSG =
|
52
|
-
|
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
|
-
|
63
|
-
|
64
|
-
|
65
|
-
end
|
65
|
+
class GetMotorPosition < SimpleRequest
|
66
|
+
MSG = 0x0c
|
67
|
+
end
|
66
68
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
end
|
69
|
+
class GetMotorRollingSpeed < SimpleRequest
|
70
|
+
MSG = 0x23
|
71
|
+
end
|
71
72
|
|
72
|
-
|
73
|
-
|
74
|
-
end
|
73
|
+
class GetMotorStatus < SimpleRequest
|
74
|
+
MSG = 0x0e
|
75
75
|
end
|
76
76
|
|
77
|
-
class
|
78
|
-
MSG =
|
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
|