somfy_sdn 1.0.11 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,35 @@
1
+ module SDN
2
+ class Message
3
+ module ILT2
4
+ class MasterControl
5
+ class << self
6
+ include Helpers
7
+
8
+ def parse(data)
9
+ return unless data.length >= 5
10
+ return unless checksum(data[0..2]) == data[3..4]
11
+ # no clue what's special about these
12
+ return unless data[0..1] == [0xfa, 0x7a]
13
+ klass = case data[2]
14
+ when 0x00; Down
15
+ when 0xfa; Up
16
+ when 0xff; Stop
17
+ end
18
+ return unless klass
19
+ [klass.new, 5]
20
+ end
21
+ end
22
+
23
+ class Down < MasterControl
24
+ end
25
+
26
+ class Stop < MasterControl
27
+ end
28
+
29
+ class Up < MasterControl
30
+ end
31
+
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,127 @@
1
+ module SDN
2
+ class Message
3
+ module ILT2
4
+ class PostIRConfig < Message
5
+ MSG = 0x69
6
+ PARAMS_LENGTH = 1
7
+
8
+ attr_reader :channels
9
+
10
+ def initialize(channels = nil, **kwargs)
11
+ super(**kwargs)
12
+ self.channels = channels
13
+ end
14
+
15
+ def channels=(value)
16
+ @channels = value &. & 0xff
17
+ end
18
+
19
+ def parse(params)
20
+ super
21
+ self.channels = to_number(params[0])
22
+ end
23
+
24
+ def params
25
+ transform_param(channels)
26
+ end
27
+
28
+ def class_inspect
29
+ ", @channels=#{channels.chr.unpack('b8').first}"
30
+ end
31
+ end
32
+
33
+ class PostLockStatus < Message
34
+ MSG = 0x6B
35
+ PARAMS_LENGTH = 1
36
+
37
+ attr_accessor :priority # 0 for not locked
38
+
39
+ def initialize(priority = nil, **kwargs)
40
+ super(**kwargs)
41
+ self.priority = priority
42
+ end
43
+
44
+ def parse(params)
45
+ super
46
+ self.current_lock_priority = to_number(params[0])
47
+ end
48
+
49
+ def params
50
+ transform_param(priority)
51
+ end
52
+ end
53
+
54
+ class PostMotorIP < Message
55
+ MSG = 0x63
56
+ PARAMS_LENGTH = 3
57
+
58
+ attr_accessor :ip, :position_pulses
59
+
60
+ def initialize(ip = nil, position_pulses = nil, **kwargs)
61
+ super(**kwargs)
62
+ self.ip = ip
63
+ self.position_pulses = position_pulses
64
+ end
65
+
66
+ def parse(params)
67
+ super
68
+ self.ip = to_number(params[0]) + 1
69
+ self.position_pulses = to_number(params[1..2], nillable: true)
70
+ end
71
+
72
+ def params
73
+ transform_param(ip - 1) + from_number(position_pulses, 2)
74
+ end
75
+ end
76
+
77
+ class PostMotorPosition < Message
78
+ MSG = 0x64
79
+ PARAMS_LENGTH = 3
80
+
81
+ attr_accessor :position_pulses, :position_percent
82
+
83
+ def initialize(position_pulses = nil, position_percent = nil, **kwargs)
84
+ super(**kwargs)
85
+ self.position_pulses = position_pulses
86
+ self.position_percent = position_percent
87
+ end
88
+
89
+ def parse(params)
90
+ super
91
+ self.position_pulses = to_number(params[0..1])
92
+ self.position_percent = to_number(params[2]).to_f / 255 * 100
93
+ end
94
+
95
+ def params
96
+ from_number(position_pulses, 2) +
97
+ from_number(position_percent && position_percent * 255 / 100)
98
+ end
99
+ end
100
+
101
+ class PostMotorSettings < UnknownMessage
102
+ MSG = 0x62
103
+ PARAMS_LENGTH = 3
104
+
105
+ attr_accessor :limit
106
+
107
+ def initialize(limit = nil, **kwargs)
108
+ super(**kwargs)
109
+ self.limit = limit
110
+ end
111
+
112
+ def parse(params)
113
+ super
114
+ self.limit = to_number(params[1..2])
115
+ end
116
+
117
+ def params
118
+ transform_param(0) + from_number(limit, 2)
119
+ end
120
+ end
121
+
122
+ class PostNodeLabel < ::SDN::Message::PostNodeLabel
123
+ MAX_LENGTH = 32
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,192 @@
1
+ module SDN
2
+ class Message
3
+ module ILT2
4
+ class SetIRConfig < PostIRConfig
5
+ MSG = 0x59
6
+
7
+ def initialize(dest = nil, channels = nil, **kwargs)
8
+ kwargs[:dest] ||= dest
9
+ super(channels, **kwargs)
10
+ end
11
+ end
12
+
13
+ class SetLockStatus < Message
14
+ MSG = 0x5B
15
+ PARAMS_LENGTH = 3
16
+ TARGET_TYPE = {
17
+ current: 0,
18
+ up_limit: 1,
19
+ down_limit: 2,
20
+ ip: 4,
21
+ unlock: 5
22
+ }
23
+
24
+ # when target_type is down_limit, target is number of 10ms intervals it's still allowed to roll up
25
+ attr_reader :target_type, :target, :priority
26
+
27
+ def initialize(dest = nil, target_type = :unlock, target = nil, priority = 1, **kwargs)
28
+ kwargs[:dest] ||= dest
29
+ super(**kwargs)
30
+ self.target_type = target_type
31
+ self.target = target
32
+ self.priority = priority
33
+ end
34
+
35
+ def parse(params)
36
+ super
37
+ self.target_type = TARGET_TYPE.invert[to_number(params[0])]
38
+ self.target = to_number(params[1])
39
+ self.priority = to_number(params[2])
40
+ end
41
+
42
+ def target_type=(value)
43
+ raise ArgumentError, "target_type must be one of :current, :up_limit, :down_limit, :ip, or :unlock" unless TARGET_TYPE.keys.include?(value)
44
+ @target_type = value
45
+ end
46
+
47
+ def target=(value)
48
+ @target = value&. & 0xff
49
+ end
50
+
51
+ def priority=(value)
52
+ raise ArgumentError, "priority must be between 1 and 100" unless (1..100).include?(value)
53
+ @priority = value
54
+ end
55
+
56
+ def params
57
+ transform_param(TARGET_TYPE[target_type]) + transform_param(target) + transform_param(priority)
58
+ end
59
+ end
60
+
61
+ class SetMotorIP < Message
62
+ MSG = 0x53
63
+ PARAMS_LENGTH = 3
64
+
65
+ attr_reader :ip, :value
66
+
67
+ def initialize(dest = nil, ip = 1, value = nil, **kwargs)
68
+ kwargs[:dest] ||= dest
69
+ super(**kwargs)
70
+ self.ip = ip
71
+ self.value = value
72
+ end
73
+
74
+ def parse(params)
75
+ super
76
+ self.ip = to_number(params[0]) + 1
77
+ self.value = to_number(params[1..2], nillable: true)
78
+ end
79
+
80
+ def ip=(value)
81
+ raise ArgumentError, "ip must be in range 1..16 or nil" unless ip.nil? || (1..16).include?(ip)
82
+ @ip = value
83
+ end
84
+
85
+ def value=(value)
86
+ @value = value &. & 0xffff
87
+ end
88
+
89
+ def params
90
+ transform_param(ip - 1) + from_number(value, 2)
91
+ end
92
+ end
93
+
94
+ class SetMotorLimits < UnknownMessage
95
+ MSG = 0x5C
96
+ end
97
+
98
+ class SetMotorPosition < Message
99
+ MSG = 0x54
100
+ PARAMS_LENGTH = 3
101
+ TARGET_TYPE = {
102
+ up_limit: 1,
103
+ down_limit: 2,
104
+ stop: 3,
105
+ ip: 4,
106
+ next_ip_up: 5,
107
+ next_ip_down: 6,
108
+ position_pulses: 8,
109
+ jog_up_ms: 10,
110
+ jog_down_ms: 11,
111
+ jog_up_pulses: 12,
112
+ jog_down_pulses: 13,
113
+ position_percent: 16,
114
+ }.freeze
115
+
116
+ attr_reader :target_type, :target
117
+
118
+ def initialize(dest = nil, target_type = :up_limit, target = 0, **kwargs)
119
+ kwargs[:dest] ||= dest
120
+ 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])
129
+ if target_type == :position_percent
130
+ target = target.to_f / 255 * 100
131
+ end
132
+ if target_type == :ip
133
+ target += 1
134
+ end
135
+ self.target = target
136
+ end
137
+
138
+ def target_type=(value)
139
+ raise ArgumentError, "target_type must be one of :up_limit, :down_limit, :stop, :ip, :next_ip_up, :next_ip_down, :jog_up, :jog_down, or :position_percent" unless TARGET_TYPE.keys.include?(value)
140
+ @target_type = value
141
+ end
142
+
143
+ def target=(value)
144
+ if target_type == :position_percent && value
145
+ @target = [[0, value].max, 100].min
146
+ else
147
+ @target = value&. & 0xffff
148
+ end
149
+ end
150
+
151
+ def params
152
+ param = target
153
+ param = (param * 255 / 100).to_i if target_type == :position_percent
154
+ param -= 1 if target_type == :ip
155
+ transform_param(TARGET_TYPE[target_type]) + from_number(param, 2)
156
+ end
157
+ end
158
+
159
+ # the motor does not move, and just stores the new values
160
+ # flags of 1 is reverse direction, but you have to set it every time
161
+ class SetMotorSettings < UnknownMessage
162
+ MSG = 0x52
163
+ PARAMS_LENGTH = 5
164
+
165
+ attr_accessor :flags, :down_limit, :position_pulses
166
+
167
+ def initialize(dest = nil, flags = 0, down_limit = 0, position_pulses = 0, **kwargs)
168
+ kwargs[:dest] ||= dest
169
+ super(**kwargs)
170
+ self.flags = flags
171
+ self.down_limit = down_limit
172
+ self.position_pulses = position_pulses
173
+ end
174
+
175
+ def parse(params)
176
+ super
177
+ self.flags = to_number(params[0])
178
+ self.down_limit = to_number(params[1..2])
179
+ self.position_pulses = to_number(params[3..4])
180
+ end
181
+
182
+ def params
183
+ transform_param(flags) + from_number(down_limit, 2) + from_number(position_pulses, 2)
184
+ end
185
+ end
186
+
187
+ class SetNodeLabel < ::SDN::Message::SetNodeLabel
188
+ MAX_LENGTH = 32
189
+ end
190
+ end
191
+ end
192
+ end
@@ -1,44 +1,68 @@
1
1
  module SDN
2
2
  class Message
3
- class PostMotorPosition < Message
4
- MSG = 0x0d
5
- PARAMS_LENGTH = 5
3
+ class PostGroupAddr < Message
4
+ MSG = 0x61
5
+ PARAMS_LENGTH = 4
6
6
 
7
- attr_accessor :position_pulses, :position_percent, :ip
7
+ def initialize(group_index = nil, group_address = nil, **kwargs)
8
+ super(**kwargs)
9
+ self.group_index = group_index
10
+ self.group_address = group_address
11
+ end
12
+
13
+ attr_accessor :group_index, :group_address
8
14
 
9
15
  def parse(params)
10
16
  super
11
- self.position_pulses = to_number(params[0..1], nillable: true)
12
- self.position_percent = to_number(params[2], nillable: true)
13
- self.ip = to_number(params[4], nillable: true)
17
+ self.group_index = to_number(params[0]) + 1
18
+ self.group_address = transform_param(params[1..3])
19
+ self.group_address = nil if group_address == [0, 0, 0] || group_address == [0x01, 0x01, 0xff]
20
+ end
21
+
22
+ def params
23
+ from_number(group_index - 1) + transform_param(group_address || [0, 0, 0])
24
+ end
25
+
26
+ def class_inspect
27
+ ", group_index=#{group_index.inspect}, group_address=#{group_address ? print_address(group_address) : 'nil'}"
14
28
  end
15
29
  end
16
30
 
17
- class PostMotorStatus < Message
18
- MSG = 0x0f
31
+ class PostMotorDirection < Message
32
+ MSG = 0x32
33
+ PARAMS_LENGTH = 1
34
+ DIRECTION = { standard: 0x00, reversed: 0x01 }.freeze
35
+
36
+ attr_accessor :direction
37
+
38
+ def parse(params)
39
+ super
40
+ self.direction = DIRECTION.invert[to_number(params[0])]
41
+ end
42
+ end
43
+
44
+ class PostMotorIP < Message
45
+ MSG = 0x35
19
46
  PARAMS_LENGTH = 4
20
- STATE = { stopped: 0x00, running: 0x01, blocked: 0x02, locked: 0x03 }.freeze
21
- DIRECTION = { down: 0x00, up: 0x01 }.freeze
22
- SOURCE = { internal: 0x00, network: 0x01, dct: 0x02 }.freeze
23
- CAUSE = { target_reached: 0x00,
24
- explicit_command: 0x01,
25
- wink: 0x02,
26
- limits_not_set: 0x10,
27
- ip_not_set: 0x11,
28
- polarity_not_checked: 0x12,
29
- in_configuration_mode: 0x13,
30
- obstacle_detection: 0x20,
31
- over_current_protection: 0x21,
32
- thermal_protection: 0x22 }.freeze
33
47
 
34
- attr_accessor :state, :last_direction, :last_action_source, :last_action_cause
48
+ attr_accessor :ip, :position_pulses, :position_percent
49
+
50
+ def initialize(ip = nil, position_pulses = nil, position_percent = nil, **kwargs)
51
+ super(**kwargs)
52
+ self.ip = ip
53
+ self.position_pulses = position_pulses
54
+ self.position_percent = position_percent
55
+ end
35
56
 
36
57
  def parse(params)
37
58
  super
38
- self.state = STATE.invert[to_number(params[0])]
39
- self.last_direction = DIRECTION.invert[to_number(params[1])]
40
- self.last_action_source = SOURCE.invert[to_number(params[2])]
41
- self.last_action_cause = CAUSE.invert[to_number(params[3])]
59
+ self.ip = to_number(params[0])
60
+ self.position_pulses = to_number(params[1..2], nillable: true)
61
+ self.position_percent = to_number(params[3], nillable: true)
62
+ end
63
+
64
+ def params
65
+ from_number(ip) + from_number(position_pulses, 2) + from_number(position_percent)
42
66
  end
43
67
  end
44
68
 
@@ -48,23 +72,45 @@ module SDN
48
72
 
49
73
  attr_accessor :up_limit, :down_limit
50
74
 
75
+ def initialize(up_limit = nil, down_limit = nil, **kwargs)
76
+ super(**kwargs)
77
+ self.up_limit = up_limit
78
+ self.down_limit = down_limit
79
+ end
80
+
51
81
  def parse(params)
52
82
  super
53
83
  self.up_limit = to_number(params[0..1], nillable: true)
54
84
  self.down_limit = to_number(params[2..3], nillable: true)
55
85
  end
86
+
87
+ def params
88
+ from_number(up_limit, 2) + from_number(down_limit, 2)
89
+ end
56
90
  end
57
91
 
58
- class PostMotorDirection < Message
59
- MSG = 0x32
60
- PARAMS_LENGTH = 1
61
- DIRECTION = { standard: 0x00, reversed: 0x01 }.freeze
92
+ class PostMotorPosition < Message
93
+ MSG = 0x0d
94
+ PARAMS_LENGTH = 5
62
95
 
63
- attr_accessor :direction
96
+ attr_accessor :position_pulses, :position_percent, :ip
97
+
98
+ def initialize(position_pulses = nil, position_percent = nil, ip = nil, **kwargs)
99
+ super(**kwargs)
100
+ self.position_pulses = position_pulses
101
+ self.position_percent = position_percent
102
+ self.ip = ip
103
+ end
64
104
 
65
105
  def parse(params)
66
106
  super
67
- self.direction = DIRECTION.invert[to_number(params[0])]
107
+ self.position_pulses = to_number(params[0..1], nillable: true)
108
+ self.position_percent = to_number(params[2], nillable: true)
109
+ self.ip = to_number(params[4], nillable: true)
110
+ end
111
+
112
+ def params
113
+ from_number(position_pulses, 2) + from_number(position_percent) + from_number(ip)
68
114
  end
69
115
  end
70
116
 
@@ -83,17 +129,40 @@ module SDN
83
129
  end
84
130
  end
85
131
 
86
- class PostMotorIP < Message
87
- MSG = 0x35
132
+ class PostMotorStatus < Message
133
+ MSG = 0x0f
88
134
  PARAMS_LENGTH = 4
135
+ STATE = { stopped: 0x00, running: 0x01, blocked: 0x02, locked: 0x03 }.freeze
136
+ DIRECTION = { down: 0x00, up: 0x01 }.freeze
137
+ SOURCE = { internal: 0x00, network: 0x01, dct: 0x02 }.freeze
138
+ CAUSE = { target_reached: 0x00,
139
+ explicit_command: 0x01,
140
+ wink: 0x02,
141
+ limits_not_set: 0x10,
142
+ ip_not_set: 0x11,
143
+ polarity_not_checked: 0x12,
144
+ in_configuration_mode: 0x13,
145
+ obstacle_detection: 0x20,
146
+ over_current_protection: 0x21,
147
+ thermal_protection: 0x22 }.freeze
89
148
 
90
- attr_accessor :ip, :position_pulses, :position_percent
149
+ attr_accessor :state, :last_direction, :last_action_source, :last_action_cause
150
+
151
+ def parse(params)
152
+ super
153
+ self.state = STATE.invert[to_number(params[0])]
154
+ self.last_direction = DIRECTION.invert[to_number(params[1])]
155
+ self.last_action_source = SOURCE.invert[to_number(params[2])]
156
+ self.last_action_cause = CAUSE.invert[to_number(params[3])]
157
+ end
158
+ end
159
+
160
+ class PostNetworkLock < UnknownMessage
161
+ MSG = 0x36
162
+ PARAMS_LENGTH = 5
91
163
 
92
164
  def parse(params)
93
165
  super
94
- self.ip = to_number(params[0])
95
- self.position_pulses = to_number(params[1..2], nillable: true)
96
- self.position_percent = to_number(params[3], nillable: true)
97
166
  end
98
167
  end
99
168
 
@@ -102,32 +171,39 @@ module SDN
102
171
  PARAMS_LENGTH = 0
103
172
  end
104
173
 
105
- class PostGroupAddr < Message
106
- MSG = 0x61
107
- PARAMS_LENGTH = 4
174
+ class PostNodeAppVersion < Message
175
+ MSG = 0x75
176
+ PARAMS_LENGTH = 6
108
177
 
109
- attr_accessor :group_index, :group_address
178
+ attr_accessor :reference, :index_letter, :index_number, :profile
110
179
 
111
180
  def parse(params)
112
181
  super
113
- self.group_index = to_number(params[0])
114
- self.group_address = transform_param(params[1..3])
115
- self.group_address = nil if group_address == [0, 0, 0] || group_address == [0x01, 0x01, 0xff]
116
- end
117
-
118
- def class_inspect
119
- ", group_index=#{group_index.inspect}, group_address=#{group_address ? print_address(group_address) : 'nil'}"
182
+ self.reference = to_number(params[0..2])
183
+ self.index_letter = to_string(params[3..3])
184
+ self.index_number = transform_param(params[4])
185
+ self.profile = transform_param(params[5])
120
186
  end
121
187
  end
122
188
 
123
189
  class PostNodeLabel < Message
124
190
  MSG = 0x65
191
+ MAX_LENGTH = 16
125
192
 
126
193
  attr_accessor :label
127
194
 
195
+ def initialize(label = nil, **kwargs)
196
+ super(**kwargs)
197
+ self.label = label
198
+ end
199
+
128
200
  def parse(params)
129
201
  @label = to_string(params)
130
202
  end
203
+
204
+ def params
205
+ from_string(label, self.class::MAX_LENGTH)
206
+ end
131
207
  end
132
208
 
133
209
  class PostNodeSerialNumber < Message
@@ -145,18 +221,8 @@ module SDN
145
221
  end
146
222
  end
147
223
 
148
- class PostNodeStackVersion < UnknownMessage
224
+ class PostNodeStackVersion < PostNodeAppVersion
149
225
  MSG = 0x71
150
-
151
- attr_accessor :params
152
-
153
- def parse(params)
154
- # I don't know how to interpret this yet
155
- # I get b6 bc b2 be fc fe, and UAI+ shows 5063497A3
156
- super
157
- end
158
-
159
- def msg; MSG; end
160
226
  end
161
227
  end
162
228
  end