somfy_sdn 1.0.7 → 1.0.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4edace9439651dd89a2c7618a03ae5b6ac9d4ccdf8101ac597eca8f5e66bacf9
4
- data.tar.gz: 4ecefc5e15483f4b97169f3a0e8939297c1bdd4cdbe909a2388806feaa0bbac7
3
+ metadata.gz: 66516a3bb03695b73075a496a791502683ec12052219a5cf040cfe6e9f82b04f
4
+ data.tar.gz: bad8e87688534b118e679b4126490f5eab3c6e83ee7288184368e852bf0cc450
5
5
  SHA512:
6
- metadata.gz: e515961e3b633fd7d02879b4d907d76a7bacde2e91f20ddb90893d676e88657f89f912dd645a7e32f9cc166641ae86b816f2dbe96ea1eb702922c51a3bc1b697
7
- data.tar.gz: 5825179cad7e2dd32af3a9bbca82cf706dbd0e38b10fe0a756241f8e28e954b414a37fe1a80fa0a84e0586f515b379d27a6b0ab013e9a77a71519924ae68e4a0
6
+ metadata.gz: db18252c03d784aeb3461dacffc16dae04aeb176f5b98f3b796d395f95980350576075a434794e16347eef7bfb308eccf7e4307abeceb4675254a3505d74b60d
7
+ data.tar.gz: e17af388921091abe51a23b6fcef7c3bf0d402ad55446bfc86aa698482a22cca341f7612b73d04e97ad91e060ff13f7573016e4ddb7aca24002c69f1ee9706b9
@@ -0,0 +1,6 @@
1
+ require 'sdn/message'
2
+ require 'sdn/mqtt_bridge'
3
+
4
+ module SDN
5
+ BROADCAST_ADDRESS = [0xff, 0xff, 0xff]
6
+ end
@@ -3,7 +3,7 @@ require 'sdn/messages/helpers'
3
3
  module SDN
4
4
  class MalformedMessage < RuntimeError; end
5
5
 
6
- class Message
6
+ class Message
7
7
  class << self
8
8
  def parse(data)
9
9
  offset = -1
@@ -11,7 +11,7 @@ module SDN
11
11
  # we loop here scanning for a valid message
12
12
  loop do
13
13
  offset += 1
14
- return nil if data.length - offset < 11
14
+ return [nil, 0] if data.length - offset < 11
15
15
  msg = to_number(data[offset])
16
16
  length = to_number(data[offset + 1])
17
17
  ack_requested = length & 0x80 == 0x80
@@ -39,9 +39,14 @@ module SDN
39
39
  reserved = to_number(data[offset + 2])
40
40
  src = transform_param(data.slice(offset + 3, 3))
41
41
  dest = transform_param(data.slice(offset + 6, 3))
42
- result = message_class.new(reserved: reserved, ack_requested: ack_requested, src: src, dest: dest)
43
- result.parse(data.slice(offset + 9, length - 11))
44
- result.msg = msg if message_class == UnknownMessage
42
+ begin
43
+ result = message_class.new(reserved: reserved, ack_requested: ack_requested, src: src, dest: dest)
44
+ result.parse(data.slice(offset + 9, length - 11))
45
+ result.msg = msg if message_class == UnknownMessage
46
+ rescue ArgumentError => e
47
+ puts "discarding illegal message #{e}"
48
+ result = nil
49
+ end
45
50
  [result, offset + length]
46
51
  end
47
52
  end
@@ -72,10 +77,13 @@ module SDN
72
77
  length |= 0x80 if ack_requested
73
78
  result = transform_param(self.class.const_get(:MSG)) + transform_param(length) + result
74
79
  result.concat(checksum(result))
75
- puts "wrote #{result.map { |b| '%02x' % b }.join(' ')}"
76
80
  result.pack("C*")
77
81
  end
78
82
 
83
+ def ==(other)
84
+ self.serialize == other.serialize
85
+ end
86
+
79
87
  def inspect
80
88
  "#<%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]
81
89
  end
@@ -27,17 +27,17 @@ module SDN
27
27
  end
28
28
 
29
29
  def direction=(value)
30
- raise ArgumentError, "direction must be one of :down, :up, or :cancel" unless DIRECTION.keys.include?(value)
30
+ raise ArgumentError, "direction must be one of :down, :up, or :cancel (#{value})" unless DIRECTION.keys.include?(value)
31
31
  @direction = value
32
32
  end
33
33
 
34
34
  def duration=(value)
35
- raise ArgumentError, "duration must be in range 0x0a to 0xff" unless value || value >= 0x0a && value <= 0xff
35
+ raise ArgumentError, "duration must be in range 0x0a to 0xff (#{value})" unless value && value >= 0x0a && value <= 0xff
36
36
  @duration = value
37
37
  end
38
38
 
39
39
  def speed=(value)
40
- raise ArgumentError, "speed must be one of :up, :down, or :slow" unless SPEED.keys.include?(value)
40
+ raise ArgumentError, "speed must be one of :up, :down, or :slow (#{value})" unless SPEED.keys.include?(value)
41
41
  @speed = speed
42
42
  end
43
43
 
@@ -0,0 +1,9 @@
1
+ module SDN
2
+ class Message
3
+ module ILT2
4
+ class GetMotorPosition < SimpleRequest
5
+ MSG = 0x44
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ module SDN
2
+ class Message
3
+ module ILT2
4
+ class PostMotorPosition < Message
5
+ MSG = 0x64
6
+ PARAMS_LENGTH = 3
7
+
8
+ attr_accessor :position_pulses, :position_percent
9
+
10
+ def parse(params)
11
+ super
12
+ self.position_pulses = to_number(params[0..1])
13
+ self.position_percent = to_number(params[2]).to_f / 255 * 100
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,59 @@
1
+ module SDN
2
+ class Message
3
+ module ILT2
4
+ class SetMotorPosition < Message
5
+ MSG = 0x54
6
+ PARAMS_LENGTH = 3
7
+ TARGET_TYPE = {
8
+ up_limit: 1,
9
+ down_limit: 2,
10
+ stop: 3,
11
+ ip: 4,
12
+ next_ip_up: 5,
13
+ next_ip_down: 6,
14
+ jog_up: 10,
15
+ jog_down: 11,
16
+ position_percent: 16,
17
+ }.freeze
18
+
19
+ attr_reader :target_type, :target
20
+
21
+ def initialize(dest = nil, target_type = :up_limit, target = 0, **kwargs)
22
+ kwargs[:dest] ||= dest
23
+ super(**kwargs)
24
+ self.target_type = target_type
25
+ self.target = target
26
+ end
27
+
28
+ def parse(params)
29
+ super
30
+ self.target_type = TARGET_TYPE.invert[to_number(params[0])]
31
+ target = to_number(params[1..2])
32
+ if target_type == :position_percent
33
+ target = target.to_f / 255 * 100
34
+ end
35
+ self.target = target
36
+ end
37
+
38
+ def target_type=(value)
39
+ 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)
40
+ @target_type = value
41
+ end
42
+
43
+ def target=(value)
44
+ if target_type == :position_percent && value
45
+ @target = [[0, value].max, 100].min
46
+ else
47
+ @target = value&. & 0xffff
48
+ end
49
+ end
50
+
51
+ def params
52
+ param = target
53
+ param = (param * 255 / 100).to_i if target_type == :position_percent
54
+ transform_param(TARGET_TYPE[target_type]) + from_number(param, 2)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -3,7 +3,7 @@ require 'uri'
3
3
  require 'set'
4
4
 
5
5
  module SDN
6
- Group = Struct.new(:bridge, :addr, :positionpercent, :state) do
6
+ Group = Struct.new(:bridge, :addr, :positionpercent, :state, :motors) do
7
7
  def initialize(*)
8
8
  members.each { |k| self[k] = :nil }
9
9
  super
@@ -20,9 +20,13 @@ module SDN
20
20
  Message.print_address(Message.parse_address(addr))
21
21
  end
22
22
 
23
- def motors
23
+ def motor_objects
24
24
  bridge.motors.select { |addr, motor| motor.groups_string.include?(printed_addr) }.values
25
25
  end
26
+
27
+ def motors_string
28
+ motor_objects.map { |m| SDN::Message.print_address(SDN::Message.parse_address(m.addr)) }.sort.join(',')
29
+ end
26
30
  end
27
31
 
28
32
  Motor = Struct.new(:bridge,
@@ -74,49 +78,50 @@ module SDN
74
78
  :ip16pulses,
75
79
  :ip16percent,
76
80
  :groups) do
77
- def initialize(*)
78
- members.each { |k| self[k] = :nil }
79
- @groups = [].fill(nil, 0, 16)
80
- super
81
- end
82
-
83
- def publish(attribute, value)
84
- if self[attribute] != value
85
- bridge.publish("#{addr}/#{attribute}", value.to_s)
86
- self[attribute] = value
87
- end
88
- end
89
-
90
- def add_group(index, address)
91
- bridge.add_group(SDN::Message.print_address(address)) if address
92
- @groups[index] = address
93
- publish(:groups, groups_string)
94
- end
95
-
96
- def set_groups(groups)
97
- return unless groups =~ /^(?:\h{2}[:.]?\h{2}[:.]?\h{2}(?:,\h{2}[:.]?\h{2}[:.]?\h{2})*)?$/i
98
- groups = groups.split(',').sort.uniq.map { |g| SDN::Message.parse_address(g) }
99
- groups.fill(nil, groups.length, 16 - groups.length)
100
- messages = []
101
- sdn_addr = SDN::Message.parse_address(addr)
102
- groups.each_with_index do |g, i|
103
- if @groups[i] != g
104
- messages << SDN::Message::SetGroupAddr.new(sdn_addr, i, g)
105
- messages << SDN::Message::GetGroupAddr.new(sdn_addr, i)
106
- end
107
- end
108
- messages
109
- end
110
-
111
- def groups_string
112
- @groups.compact.map { |g| SDN::Message.print_address(g) }.sort.uniq.join(',')
113
- end
114
-
115
- def group_objects
116
- groups_string.split(',').map { |addr| bridge.groups[addr.gsub('.', '')] }
117
- end
81
+ def initialize(*)
82
+ members.each { |k| self[k] = :nil }
83
+ @groups = [].fill(nil, 0, 16)
84
+ super
85
+ end
86
+
87
+ def publish(attribute, value)
88
+ if self[attribute] != value
89
+ bridge.publish("#{addr}/#{attribute}", value.to_s)
90
+ self[attribute] = value
91
+ end
92
+ end
93
+
94
+ def add_group(index, address)
95
+ group = bridge.add_group(SDN::Message.print_address(address)) if address
96
+ @groups[index] = address
97
+ group&.publish(:motors, group.motors_string)
98
+ publish(:groups, groups_string)
99
+ end
100
+
101
+ def set_groups(groups)
102
+ return unless groups =~ /^(?:\h{2}[:.]?\h{2}[:.]?\h{2}(?:,\h{2}[:.]?\h{2}[:.]?\h{2})*)?$/i
103
+ groups = groups.split(',').sort.uniq.map { |g| SDN::Message.parse_address(g) }.select { |g| SDN::Message.is_group_address?(g) }
104
+ groups.fill(nil, groups.length, 16 - groups.length)
105
+ messages = []
106
+ sdn_addr = SDN::Message.parse_address(addr)
107
+ groups.each_with_index do |g, i|
108
+ if @groups[i] != g
109
+ messages << SDN::Message::SetGroupAddr.new(sdn_addr, i, g)
110
+ messages << SDN::Message::GetGroupAddr.new(sdn_addr, i)
111
+ end
112
+ end
113
+ messages
118
114
  end
119
115
 
116
+ def groups_string
117
+ @groups.compact.map { |g| SDN::Message.print_address(g) }.sort.uniq.join(',')
118
+ end
119
+
120
+ def group_objects
121
+ groups_string.split(',').map { |addr| bridge.groups[addr.gsub('.', '')] }
122
+ end
123
+ end
124
+
120
125
  class MQTTBridge
121
126
  WAIT_TIME = 0.25
122
127
  BROADCAST_WAIT = 5.0
@@ -161,6 +166,8 @@ module SDN
161
166
  loop do
162
167
  begin
163
168
  message, bytes_read = SDN::Message.parse(buffer.bytes)
169
+ # discard how much we read
170
+ buffer = buffer[bytes_read..-1]
164
171
  unless message
165
172
  begin
166
173
  buffer.concat(@sdn.read_nonblock(64 * 1024))
@@ -175,8 +182,6 @@ module SDN
175
182
  end
176
183
  next
177
184
  end
178
- # discard how much we read
179
- buffer = buffer[bytes_read..-1]
180
185
 
181
186
  src = SDN::Message.print_address(message.src)
182
187
  # ignore the UAI Plus and ourselves
@@ -197,7 +202,7 @@ module SDN
197
202
  motor.publish(:positionpulses, message.position_pulses)
198
203
  motor.publish(:ip, message.ip)
199
204
  motor.group_objects.each do |group|
200
- positions = group.motors.map(&:positionpercent)
205
+ positions = group.motor_objects.map(&:positionpercent)
201
206
  position = nil
202
207
  # calculate an average, but only if we know a position for
203
208
  # every shade
@@ -218,7 +223,7 @@ module SDN
218
223
  motor.publish(:last_action_source, message.last_action_source)
219
224
  motor.publish(:last_action_cause, message.last_action_cause)
220
225
  motor.group_objects.each do |group|
221
- states = group.motors.map(&:state).uniq
226
+ states = group.motor_objects.map(&:state).uniq
222
227
  state = states.length == 1 ? states.first : 'mixed'
223
228
  group.publish(:state, state)
224
229
  end
@@ -232,7 +237,7 @@ module SDN
232
237
  motor.publish(:downspeed, message.down_speed)
233
238
  motor.publish(:slowspeed, message.slow_speed)
234
239
  when SDN::Message::PostMotorIP
235
- motor.publish(:"ip#{message.ip}pulses", message.position_pulses)
240
+ motor.publish(:"ip#{message.ip}pulses", message.position_pulses)
236
241
  motor.publish(:"ip#{message.ip}percent", message.position_percent)
237
242
  when SDN::Message::PostGroupAddr
238
243
  motor.add_group(message.group_index, message.group_address)
@@ -241,7 +246,9 @@ module SDN
241
246
  @mutex.synchronize do
242
247
  signal = @response_pending || !follow_ups.empty?
243
248
  @response_pending = @broadcast_pending
244
- @request_queue.concat(follow_ups)
249
+ follow_ups.each do |follow_up|
250
+ @request_queue.push(follow_up) unless @request_queue.include?(follow_up)
251
+ end
245
252
  @cond.signal if signal
246
253
  end
247
254
  rescue EOFError
@@ -281,9 +288,9 @@ module SDN
281
288
  message = @request_queue.shift
282
289
  if message
283
290
  @response_pending = Time.now.to_f + WAIT_TIME
284
- if message.dest == [0xff, 0xff, 0xff]
291
+ if message.dest == BROADCAST_ADDRESS || SDN::Message::is_group_address?(message.src) && message.is_a?(SDN::Message::GetNodeAddr)
285
292
  @broadcast_pending = Time.now.to_f + BROADCAST_WAIT
286
- end
293
+ end
287
294
  end
288
295
  end
289
296
 
@@ -293,8 +300,10 @@ module SDN
293
300
  next unless message
294
301
 
295
302
  puts "writing #{message.inspect}"
296
- @sdn.write(message.serialize)
303
+ serialized = message.serialize
304
+ @sdn.write(serialized)
297
305
  @sdn.flush
306
+ puts "wrote #{serialized.unpack("C*").map { |b| '%02x' % b }.join(' ')}"
298
307
  end
299
308
  rescue => e
300
309
  puts "failure writing: #{e}"
@@ -310,7 +319,7 @@ module SDN
310
319
  @request_queue.push(SDN::Message::GetNodeAddr.new)
311
320
  @cond.signal
312
321
  end
313
- elsif (match = topic.match(%r{^#{Regexp.escape(@base_topic)}/(?<addr>\h{6})/(?<property>label|down|up|stop|positionpulses|positionpercent|ip|wink|reset|(?<speed_type>upspeed|downspeed|slowspeed)|uplimit|downlimit|direction|ip(?<ip>\d+)(?<ip_type>pulses|percent)|groups)/set$}))
322
+ elsif (match = topic.match(%r{^#{Regexp.escape(@base_topic)}/(?<addr>\h{6})/(?<property>discover|label|down|up|stop|positionpulses|positionpercent|ip|wink|reset|(?<speed_type>upspeed|downspeed|slowspeed)|uplimit|downlimit|direction|ip(?<ip>\d+)(?<ip_type>pulses|percent)|groups)/set$}))
314
323
  addr = SDN::Message.parse_address(match[:addr])
315
324
  property = match[:property]
316
325
  # not homie compliant; allows linking the positionpercent property
@@ -319,10 +328,15 @@ module SDN
319
328
  property = value.downcase
320
329
  value = "true"
321
330
  end
322
- motor = @motors[SDN::Message.print_address(addr).gsub('.', '')]
331
+ mqtt_addr = SDN::Message.print_address(addr).gsub('.', '')
332
+ motor = @motors[mqtt_addr]
323
333
  is_group = SDN::Message.is_group_address?(addr)
334
+ group = @groups[mqtt_addr]
324
335
  follow_up = SDN::Message::GetMotorStatus.new(addr)
325
336
  message = case property
337
+ when 'discover'
338
+ follow_up = nil
339
+ SDN::Message::GetNodeAddr.new(addr) if value == "true"
326
340
  when 'label'
327
341
  follow_up = SDN::Message::GetNodeLabel.new(addr)
328
342
  SDN::Message::SetNodeLabel.new(addr, value) unless is_group
@@ -432,7 +446,7 @@ module SDN
432
446
  publish("discovery/discover/$settable", "true")
433
447
  publish("discovery/discover/$retained", "false")
434
448
 
435
- subscribe("discovery/discover/set")
449
+ subscribe("+/discover/set")
436
450
  subscribe("+/label/set")
437
451
  subscribe("+/down/set")
438
452
  subscribe("+/up/set")
@@ -460,7 +474,12 @@ module SDN
460
474
  def publish_motor(addr)
461
475
  publish("#{addr}/$name", addr)
462
476
  publish("#{addr}/$type", "Sonesse 30 Motor")
463
- publish("#{addr}/$properties", "label,down,up,stop,positionpulses,positionpercent,ip,wink,reset,state,last_direction,last_action_source,last_action_cause,uplimit,downlimit,direction,upspeed,downspeed,slowspeed,#{(1..16).map { |ip| "ip#{ip}pulses,ip#{ip}percent" }.join(',')},groups")
477
+ publish("#{addr}/$properties", "discover,label,down,up,stop,positionpulses,positionpercent,ip,wink,reset,state,last_direction,last_action_source,last_action_cause,uplimit,downlimit,direction,upspeed,downspeed,slowspeed,#{(1..16).map { |ip| "ip#{ip}pulses,ip#{ip}percent" }.join(',')},groups")
478
+
479
+ publish("#{addr}/discover/$name", "Trigger Motor Discovery")
480
+ publish("#{addr}/discover/$datatype", "boolean")
481
+ publish("#{addr}/discover/$settable", "true")
482
+ publish("#{addr}/discover/$retained", "false")
464
483
 
465
484
  publish("#{addr}/label/$name", "Node label")
466
485
  publish("#{addr}/label/$datatype", "string")
@@ -580,7 +599,7 @@ module SDN
580
599
 
581
600
  motor = Motor.new(self, addr)
582
601
  @motors[addr] = motor
583
- publish("$nodes", (["discovery"] + @motors.keys.sort + @groups.keys.sort.to_a).join(","))
602
+ publish("$nodes", (["discovery"] + @motors.keys.sort + @groups.keys.sort).join(","))
584
603
 
585
604
  sdn_addr = SDN::Message.parse_address(addr)
586
605
  @mutex.synchronize do
@@ -600,11 +619,17 @@ module SDN
600
619
 
601
620
  def add_group(addr)
602
621
  addr = addr.gsub('.', '')
603
- return if @groups.key?(addr)
622
+ group = @groups[addr]
623
+ return group if group
604
624
 
605
625
  publish("#{addr}/$name", addr)
606
626
  publish("#{addr}/$type", "Shade Group")
607
- publish("#{addr}/$properties", "down,up,stop,positionpulses,positionpercent,ip,wink,reset")
627
+ publish("#{addr}/$properties", "discover,down,up,stop,positionpulses,positionpercent,ip,wink,reset,state,motors")
628
+
629
+ publish("#{addr}/discover/$name", "Trigger Motor Discovery")
630
+ publish("#{addr}/discover/$datatype", "boolean")
631
+ publish("#{addr}/discover/$settable", "true")
632
+ publish("#{addr}/discover/$retained", "false")
608
633
 
609
634
  publish("#{addr}/down/$name", "Move in down direction")
610
635
  publish("#{addr}/down/$datatype", "boolean")
@@ -619,7 +644,7 @@ module SDN
619
644
  publish("#{addr}/stop/$name", "Cancel adjustments")
620
645
  publish("#{addr}/stop/$datatype", "boolean")
621
646
  publish("#{addr}/stop/$settable", "true")
622
- publish("#{addr}/stop/$retained", "false")
647
+ publish("#{addr}/stop/$retained", "false")
623
648
 
624
649
  publish("#{addr}/positionpulses/$name", "Position from up limit (in pulses)")
625
650
  publish("#{addr}/positionpulses/$datatype", "integer")
@@ -647,8 +672,12 @@ module SDN
647
672
  publish("#{addr}/state/$datatype", "enum")
648
673
  publish("#{addr}/state/$format", SDN::Message::PostMotorStatus::STATE.keys.join(','))
649
674
 
650
- @groups[addr] = Group.new(self, addr)
651
- publish("$nodes", (["discovery"] + @motors.keys.sort + @groups.to_a).join(","))
675
+ publish("#{addr}/motors/$name", "Motors that are members of this group")
676
+ publish("#{addr}/motors/$datatype", "string")
677
+
678
+ group = @groups[addr] = Group.new(self, addr)
679
+ publish("$nodes", (["discovery"] + @motors.keys.sort + @groups.keys.sort).join(","))
680
+ group
652
681
  end
653
682
  end
654
683
  end
@@ -1,3 +1,3 @@
1
1
  module SDN
2
- VERSION = '1.0.7'
2
+ VERSION = '1.0.8'
3
3
  end
@@ -1,2 +1 @@
1
- require 'sdn/message'
2
- require 'sdn/mqtt_bridge'
1
+ require 'sdn'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: somfy_sdn
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.7
4
+ version: 1.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cody Cutrer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-15 00:00:00.000000000 Z
11
+ date: 2020-07-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mqtt
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.0.2
33
+ version: 0.0.3
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.0.2
40
+ version: 0.0.3
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: serialport
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -88,10 +88,14 @@ extensions: []
88
88
  extra_rdoc_files: []
89
89
  files:
90
90
  - bin/sdn_mqtt_bridge
91
+ - lib/sdn.rb
91
92
  - lib/sdn/message.rb
92
93
  - lib/sdn/messages/control.rb
93
94
  - lib/sdn/messages/get.rb
94
95
  - lib/sdn/messages/helpers.rb
96
+ - lib/sdn/messages/ilt2/get.rb
97
+ - lib/sdn/messages/ilt2/post.rb
98
+ - lib/sdn/messages/ilt2/set.rb
95
99
  - lib/sdn/messages/post.rb
96
100
  - lib/sdn/messages/set.rb
97
101
  - lib/sdn/mqtt_bridge.rb