somfy_sdn 1.0.12 → 2.1.2

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.
@@ -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