sphero_pwn 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/bin/sphero_diagnostics.rb +71 -0
  4. data/bin/sphero_map_flash.rb +82 -0
  5. data/bin/sphero_save_flash_block.rb +47 -0
  6. data/bin/sphero_set_flag.rb +38 -0
  7. data/bin/sphero_soft_reboot.rb +33 -0
  8. data/bin/sphero_toggle_user_hack.rb +36 -0
  9. data/lib/sphero_pwn.rb +24 -14
  10. data/lib/sphero_pwn/asyncs/flash_block.rb +26 -0
  11. data/lib/sphero_pwn/channel.rb +72 -29
  12. data/lib/sphero_pwn/commands/boot_main_app.rb +17 -0
  13. data/lib/sphero_pwn/commands/enter_bootloader.rb +15 -0
  14. data/lib/sphero_pwn/commands/get_device_mode.rb +35 -0
  15. data/lib/sphero_pwn/commands/get_flash_block.rb +34 -0
  16. data/lib/sphero_pwn/commands/get_permanent_flags.rb +44 -0
  17. data/lib/sphero_pwn/commands/get_versions.rb +12 -10
  18. data/lib/sphero_pwn/commands/is_page_blank.rb +36 -0
  19. data/lib/sphero_pwn/commands/l1_diagnostics.rb +0 -1
  20. data/lib/sphero_pwn/commands/l2_diagnostics.rb +50 -0
  21. data/lib/sphero_pwn/commands/set_device_mode.rb +25 -0
  22. data/lib/sphero_pwn/commands/set_permanent_flags.rb +29 -0
  23. data/lib/sphero_pwn/session.rb +12 -4
  24. data/sphero_pwn.gemspec +130 -0
  25. data/test/command_test.rb +10 -0
  26. data/test/commands/boot_main_app_test.rb +11 -0
  27. data/test/commands/enter_bootloader_test.rb +33 -0
  28. data/test/commands/get_device_mode_test.rb +40 -0
  29. data/test/commands/get_flash_block_test.rb +101 -0
  30. data/test/commands/get_permanent_flags_test.rb +48 -0
  31. data/test/commands/get_versions_test.rb +10 -1
  32. data/test/commands/is_page_blank_test.rb +65 -0
  33. data/test/commands/l2_diagnostics_test.rb +58 -0
  34. data/test/commands/set_device_mode_test.rb +56 -0
  35. data/test/commands/set_permanent_flags_test.rb +55 -0
  36. data/test/data/enter_bootloader.txt +4 -0
  37. data/test/data/get_device_mode.txt +2 -0
  38. data/test/data/get_factory_config.txt +2 -0
  39. data/test/data/get_permanent_flags.txt +2 -0
  40. data/test/data/get_soul.txt +2 -0
  41. data/test/data/get_version.txt +2 -2
  42. data/test/data/is_page_blank.txt +6 -0
  43. data/test/data/l2_diagnostics.txt +8 -0
  44. data/test/data/ping.txt +2 -2
  45. data/test/data/set_device_mode.txt +12 -0
  46. data/test/data/set_permanent_flags.txt +8 -0
  47. metadata +44 -3
@@ -0,0 +1,17 @@
1
+ # Asks the robot's bootloader to boot the main application.
2
+ #
3
+ # This command is only valid when the robot is executing the bootloader.
4
+ class SpheroPwn::Commands::BootMainApp < SpheroPwn::Command
5
+ def initialize
6
+ super 0x01, 0x04, nil
7
+ end
8
+
9
+ # @see {SpheroPwn::Command#response_class}
10
+ def response_class
11
+ SpheroPwn::Commands::BootMainApp::Response
12
+ end
13
+ end
14
+
15
+ # The response to a boot main application command.
16
+ class SpheroPwn::Commands::BootMainApp::Response < SpheroPwn::Response
17
+ end
@@ -0,0 +1,15 @@
1
+ # Asks the robot to jump into its bootloader.
2
+ class SpheroPwn::Commands::EnterBootloader < SpheroPwn::Command
3
+ def initialize
4
+ super 0x00, 0x30, nil
5
+ end
6
+
7
+ # @see {SpheroPwn::Command#response_class}
8
+ def response_class
9
+ SpheroPwn::Commands::EnterBootloader::Response
10
+ end
11
+ end
12
+
13
+ # The response to an enter bootloader command.
14
+ class SpheroPwn::Commands::EnterBootloader::Response < SpheroPwn::Response
15
+ end
@@ -0,0 +1,35 @@
1
+ # Asks the robot about the versions of its software stack components.
2
+ class SpheroPwn::Commands::GetDeviceMode < SpheroPwn::Command
3
+ def initialize
4
+ super 0x02, 0x44, nil
5
+ end
6
+
7
+ # @see {SpheroPwn::Command#response_class}
8
+ def response_class
9
+ SpheroPwn::Commands::GetDeviceMode::Response
10
+ end
11
+ end
12
+
13
+ # The development mode of the robot.
14
+ class SpheroPwn::Commands::GetDeviceMode::Response < SpheroPwn::Response
15
+ # @return {Symbol} the device's mode; can be :normal or :user_hack
16
+ attr_reader :mode
17
+
18
+ # @see {SpheroPwn::Response#initialize}
19
+ def initialize(code_byte, sequence_byte, data_bytes)
20
+ super
21
+
22
+ if code == :ok
23
+ @mode = case data_bytes[0]
24
+ when 0x00
25
+ :normal
26
+ when 0x01
27
+ :user_hack
28
+ else
29
+ :unknown
30
+ end
31
+ else
32
+ @mode = :error
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,34 @@
1
+ # Asks the robot to send a configuration block from the flash memory.
2
+ class SpheroPwn::Commands::GetFlashBlock < SpheroPwn::Command
3
+ # @param {Symbol} block_type :soul, :factory_config or :user_config
4
+ def initialize(block_type)
5
+ case block_type
6
+ when :soul
7
+ command_id = 0x46
8
+ data_bytes = nil
9
+ when :factory_config
10
+ command_id = 0x40
11
+ data_bytes = [0x00]
12
+ when :user_config
13
+ command_id = 0x40
14
+ data_bytes = [0x01]
15
+ when /^block_/
16
+ command_id = 0x40
17
+ data_bytes = block_type.to_s.split('_')[1..-1].
18
+ map { |char| char.to_i(16) }
19
+ else
20
+ raise ArgumentError, "Unimplemented block type #{block_type.inspect}"
21
+ end
22
+
23
+ super 0x02, command_id, data_bytes
24
+ end
25
+
26
+ # @see {SpheroPwn::Command#response_class}
27
+ def response_class
28
+ SpheroPwn::Commands::GetFlashBlock::Response
29
+ end
30
+ end
31
+
32
+ # The response to a get flash block command.
33
+ class SpheroPwn::Commands::GetFlashBlock::Response < SpheroPwn::Response
34
+ end
@@ -0,0 +1,44 @@
1
+ # Obtains the robot's configuration flags that persist across power cycles.
2
+ class SpheroPwn::Commands::GetPermanentFlags < SpheroPwn::Command
3
+ def initialize
4
+ super 0x02, 0x36, nil
5
+ end
6
+
7
+ # @see {SpheroPwn::Command#response_class}
8
+ def response_class
9
+ SpheroPwn::Commands::GetPermanentFlags::Response
10
+ end
11
+
12
+ # @return {Hash<Number, Symbol>} symbolic values for the returned flags
13
+ FLAGS = {
14
+ 0x01 => :no_sleep_while_charging,
15
+ 0x02 => :vector_drive,
16
+ 0x04 => :no_leveling_while_charging,
17
+ 0x08 => :tail_led_always_on,
18
+ 0x10 => :motion_timeouts,
19
+ 0x20 => :demo_mode,
20
+ 0x40 => :light_double_tap,
21
+ 0x80 => :heavy_double_tap,
22
+ 0x100 => :gyro_max_async,
23
+ }.freeze
24
+ end
25
+
26
+ # The robot's configuration flags that persist across power cycles.
27
+ class SpheroPwn::Commands::GetPermanentFlags::Response < SpheroPwn::Response
28
+ # @return {Hash<Symbol, Boolean>} maps developer-friendly flag names to
29
+ # whether the corresponding bits were set in the flags field
30
+ attr_reader :flags
31
+
32
+ # @see {SpheroPwn::Response#initialize}
33
+ def initialize(code_byte, sequence_byte, data_bytes)
34
+ super
35
+
36
+ @flags = {}
37
+ if code == :ok
38
+ flags_number = data_bytes[0, 4].pack('C*').unpack('N').first
39
+ SpheroPwn::Commands::GetPermanentFlags::FLAGS.each do |mask, name|
40
+ @flags[name] = (flags_number & mask) != 0
41
+ end
42
+ end
43
+ end
44
+ end
@@ -21,16 +21,18 @@ class SpheroPwn::Commands::GetVersions::Response < SpheroPwn::Response
21
21
  super
22
22
 
23
23
  @versions = {}
24
- response_version = data_bytes[0]
25
- if response_version >= 1
26
- @versions.merge! model: data_bytes[1], hardware: data_bytes[2],
27
- sphero_app: { version: data_bytes[3], revision: data_bytes[4] },
28
- bootloader: self.class.parse_packed_nibble(data_bytes[5]),
29
- basic: self.class.parse_packed_nibble(data_bytes[6]),
30
- macros: self.class.parse_packed_nibble(data_bytes[7])
31
- end
32
- if response_version >= 2
33
- @versions.merge! api: { major: data_bytes[8], minor: data_bytes[9] }
24
+ if code == :ok
25
+ response_version = data_bytes[0]
26
+ if response_version >= 1
27
+ @versions.merge! model: data_bytes[1], hardware: data_bytes[2],
28
+ sphero_app: { version: data_bytes[3], revision: data_bytes[4] },
29
+ bootloader: self.class.parse_packed_nibble(data_bytes[5]),
30
+ basic: self.class.parse_packed_nibble(data_bytes[6]),
31
+ macros: self.class.parse_packed_nibble(data_bytes[7])
32
+ end
33
+ if response_version >= 2
34
+ @versions.merge! api: { major: data_bytes[8], minor: data_bytes[9] }
35
+ end
34
36
  end
35
37
  end
36
38
 
@@ -0,0 +1,36 @@
1
+ # Asks the robot's bootloader if a flash page is blank.
2
+ #
3
+ # This command is only valid when the robot is executing the bootloader.
4
+ class SpheroPwn::Commands::IsPageBlank < SpheroPwn::Command
5
+ # @param {Number} page_number the flash page that the command will ask the
6
+ # robot to look at
7
+ def initialize(page_number)
8
+ if page_number > 255
9
+ raise ArgumentError, "Page number #{page_number} exceeds 255"
10
+ end
11
+ super 0x01, 0x05, [page_number]
12
+ end
13
+
14
+ # @see {SpheroPwn::Command#response_class}
15
+ def response_class
16
+ SpheroPwn::Commands::IsPageBlank::Response
17
+ end
18
+ end
19
+
20
+ # The response to a boot main application command.
21
+ class SpheroPwn::Commands::IsPageBlank::Response < SpheroPwn::Response
22
+ # @return {Boolean} true if the flash memory page is blank
23
+ attr_reader :is_blank
24
+ alias_method :is_blank?, :is_blank
25
+
26
+ # @see {SpheroPwn::Response#initialize}
27
+ def initialize(code_byte, sequence_byte, data_bytes)
28
+ super
29
+
30
+ if code == :ok
31
+ @is_blank = data_bytes[0] > 0
32
+ else
33
+ @is_blank = nil
34
+ end
35
+ end
36
+ end
@@ -13,4 +13,3 @@ end
13
13
  # The response to an L1 diagnostics command.
14
14
  class SpheroPwn::Commands::L1Diagnostics::Response < SpheroPwn::Response
15
15
  end
16
-
@@ -0,0 +1,50 @@
1
+ # Asks the robot for its debugging counters.
2
+ #
3
+ # This command only works when the robot is in user hacking mode.
4
+ class SpheroPwn::Commands::L2Diagnostics < SpheroPwn::Command
5
+ def initialize
6
+ super 0x00, 0x41, nil
7
+ end
8
+
9
+ # @see {SpheroPwn::Command#response_class}
10
+ def response_class
11
+ SpheroPwn::Commands::L2Diagnostics::Response
12
+ end
13
+ end
14
+
15
+ # The response to an L2 diagnostics command.
16
+ class SpheroPwn::Commands::L2Diagnostics::Response < SpheroPwn::Response
17
+ # @return {Hash<Symbol, Number>} debugging information counters
18
+ attr_reader :counters
19
+
20
+ # @see {SpheroPwn::Response#initialize}
21
+ def initialize(code_byte, sequence_byte, data_bytes)
22
+ super
23
+
24
+ @counters = {}
25
+ if code == :ok
26
+ data_string = data_bytes.pack('C*')
27
+ response_version = data_bytes[0x02]
28
+ if response_version >= 1
29
+ @counters.merge! received_good: data_string[0x03, 4].unpack('N'),
30
+ reserved1: data_bytes[0x02],
31
+ bad_device_id: data_string[0x07, 4].unpack('N'),
32
+ bad_data_length: data_string[0x0B, 4].unpack('N'),
33
+ bad_command_id: data_string[0x0F, 4].unpack('N'),
34
+ bad_checksum: data_string[0x13, 4].unpack('N'),
35
+ rx_buffer_overrun: data_string[0x17, 4].unpack('N'),
36
+ transmitted: data_string[0x1B, 4].unpack('N'),
37
+ tx_buffer_overrun: data_string[0x1F, 4].unpack('N'),
38
+ last_boot_reason: data_bytes[0x23],
39
+ boots_by_reason: data_string[0x24, 32].unpack('n*'),
40
+ reserved2: data_string[0x44, 2].unpack('n'),
41
+ charge_count: data_string[0x46, 2].unpack('n'),
42
+ seconds_since_charge: data_string[0x48, 2].unpack('n'),
43
+ seconds_on: data_string[0x4A, 4].unpack('N'),
44
+ distance_rolled: data_string[0x4E, 4].unpack('N'),
45
+ i2c_failures: data_string[0x52, 2].unpack('n'),
46
+ gyro_adjusts: data_string[0x54, 4].unpack('N')
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,25 @@
1
+ # Asks the robot about the versions of its software stack components.
2
+ class SpheroPwn::Commands::SetDeviceMode < SpheroPwn::Command
3
+ def initialize(mode)
4
+
5
+ mode_byte = case mode
6
+ when :normal
7
+ 0x00
8
+ when :user_hack
9
+ 0x01
10
+ else
11
+ raise ArgumentError, "Unimplemented mode #{mode.inspect}"
12
+ end
13
+
14
+ super 0x02, 0x42, [mode_byte]
15
+ end
16
+
17
+ # @see {SpheroPwn::Command#response_class}
18
+ def response_class
19
+ SpheroPwn::Commands::SetDeviceMode::Response
20
+ end
21
+ end
22
+
23
+ # The versions of a robot's software stack.
24
+ class SpheroPwn::Commands::SetDeviceMode::Response < SpheroPwn::Response
25
+ end
@@ -0,0 +1,29 @@
1
+ # Sets the robot's configuration flags that persist across power cycles.
2
+ class SpheroPwn::Commands::SetPermanentFlags < SpheroPwn::Command
3
+ # @param {Hash<Symbol, Boolean>} maps developer-friendly flag names to
4
+ # whether the corresponding bits will be set in the flags field
5
+ def initialize(new_flags)
6
+ flags_number = 0
7
+ new_flags.each do |name, value|
8
+ mask = SpheroPwn::Commands::SetPermanentFlags::FLAGS[name]
9
+ if mask.nil?
10
+ raise ArgumentError, "Unknown flag #{name.inspect}"
11
+ end
12
+ flags_number |= mask if value
13
+ end
14
+
15
+ super 0x02, 0x35, [flags_number].pack('N').unpack('C*')
16
+ end
17
+
18
+ # @see {SpheroPwn::Command#response_class}
19
+ def response_class
20
+ SpheroPwn::Commands::SetPermanentFlags::Response
21
+ end
22
+
23
+ # @return {Hash<Symbol, Number>} numbers for the symbolic values
24
+ FLAGS = SpheroPwn::Commands::GetPermanentFlags::FLAGS.invert.freeze
25
+ end
26
+
27
+ # The robot's configuration flags that persist across power cycles.
28
+ class SpheroPwn::Commands::SetPermanentFlags::Response < SpheroPwn::Response
29
+ end
@@ -65,14 +65,18 @@ class SpheroPwn::Session
65
65
  # @return {Response, Async} the response read from the channel; can be nil if
66
66
  # no message was received or if the checksum verification failed
67
67
  def recv_message
68
- return nil if @channel.recv_bytes(1).ord != 0xFF
68
+ start_of_packet = @channel.recv_bytes 1
69
+ return nil if start_of_packet.empty? || start_of_packet.ord != 0xFF
69
70
 
70
71
  packet_type = @channel.recv_bytes 1
72
+ return nil if packet_type.empty?
71
73
  case packet_type.ord
72
74
  when 0xFF
73
75
  read_response
74
76
  when 0xFE
75
77
  read_async_message
78
+ else
79
+ nil
76
80
  end
77
81
  end
78
82
 
@@ -103,12 +107,14 @@ class SpheroPwn::Session
103
107
  def read_response
104
108
  header_bytes = @channel.recv_bytes(3).unpack('C*')
105
109
  response_code, sequence, data_length = *header_bytes
110
+ return nil unless data_length
106
111
 
107
112
  # It may seem that it'd be better to look up the sequence number and bail
108
113
  # early if we don't find it. However, in order to avoid misleading error
109
114
  # messages, we don't want to touch anything in the message until we know
110
115
  # that the checksum is valid.
111
- data_bytes = @channel.recv_bytes(data_length).unpack('C*')
116
+ return nil unless data = @channel.recv_bytes(data_length)
117
+ data_bytes = data.unpack 'C*'
112
118
  checksum = data_bytes.pop
113
119
  unless self.class.valid_checksum?(header_bytes, data_bytes, checksum)
114
120
  return nil
@@ -130,13 +136,15 @@ class SpheroPwn::Session
130
136
  def read_async_message
131
137
  header_bytes = @channel.recv_bytes(3).unpack('C*')
132
138
  class_id, length_msb, length_lsb = *header_bytes
133
- data_length = (length_msb << 8) | length_lsb
139
+ return nil unless length_msb && length_lsb
134
140
 
135
141
  # It may seem that it'd be better to look up the sequence number and bail
136
142
  # early if we don't find it. However, in order to avoid misleading error
137
143
  # messages, we don't want to touch anything in the message until we know
138
144
  # that the checksum is valid.
139
- data_bytes = @channel.recv_bytes(data_length).unpack('C*')
145
+ data_length = (length_msb << 8) | length_lsb
146
+ return nil unless data = @channel.recv_bytes(data_length)
147
+ data_bytes = data.unpack 'C*'
140
148
  checksum = data_bytes.pop
141
149
  unless self.class.valid_checksum?(header_bytes, data_bytes, checksum)
142
150
  return nil
@@ -0,0 +1,130 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+ # stub: sphero_pwn 0.0.1 ruby lib
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "sphero_pwn"
9
+ s.version = "0.0.1"
10
+
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib"]
13
+ s.authors = ["Victor Costan"]
14
+ s.date = "2015-12-15"
15
+ s.description = "This library is currently focused on reverse-engineering\n the undocumented parts of Sphero"
16
+ s.email = "victor@costan.us"
17
+ s.executables = ["sphero_diagnostics.rb", "sphero_map_flash.rb", "sphero_save_flash_block.rb", "sphero_set_flag.rb", "sphero_soft_reboot.rb", "sphero_toggle_user_hack.rb"]
18
+ s.extra_rdoc_files = [
19
+ "LICENSE.txt",
20
+ "README.markdown"
21
+ ]
22
+ s.files = [
23
+ ".document",
24
+ ".travis.yml",
25
+ "Gemfile",
26
+ "Gemfile.lock",
27
+ "LICENSE.txt",
28
+ "README.markdown",
29
+ "Rakefile",
30
+ "VERSION",
31
+ "bin/sphero_diagnostics.rb",
32
+ "bin/sphero_map_flash.rb",
33
+ "bin/sphero_save_flash_block.rb",
34
+ "bin/sphero_set_flag.rb",
35
+ "bin/sphero_soft_reboot.rb",
36
+ "bin/sphero_toggle_user_hack.rb",
37
+ "lib/sphero_pwn.rb",
38
+ "lib/sphero_pwn/async.rb",
39
+ "lib/sphero_pwn/asyncs.rb",
40
+ "lib/sphero_pwn/asyncs/flash_block.rb",
41
+ "lib/sphero_pwn/asyncs/l1_diagnostics.rb",
42
+ "lib/sphero_pwn/channel.rb",
43
+ "lib/sphero_pwn/channel_recorder.rb",
44
+ "lib/sphero_pwn/command.rb",
45
+ "lib/sphero_pwn/commands.rb",
46
+ "lib/sphero_pwn/commands/boot_main_app.rb",
47
+ "lib/sphero_pwn/commands/enter_bootloader.rb",
48
+ "lib/sphero_pwn/commands/get_device_mode.rb",
49
+ "lib/sphero_pwn/commands/get_flash_block.rb",
50
+ "lib/sphero_pwn/commands/get_permanent_flags.rb",
51
+ "lib/sphero_pwn/commands/get_versions.rb",
52
+ "lib/sphero_pwn/commands/is_page_blank.rb",
53
+ "lib/sphero_pwn/commands/l1_diagnostics.rb",
54
+ "lib/sphero_pwn/commands/l2_diagnostics.rb",
55
+ "lib/sphero_pwn/commands/ping.rb",
56
+ "lib/sphero_pwn/commands/set_device_mode.rb",
57
+ "lib/sphero_pwn/commands/set_permanent_flags.rb",
58
+ "lib/sphero_pwn/replay_channel.rb",
59
+ "lib/sphero_pwn/response.rb",
60
+ "lib/sphero_pwn/session.rb",
61
+ "lib/sphero_pwn/test_channel.rb",
62
+ "sphero_pwn.gemspec",
63
+ "test/channel_recorder_test.rb",
64
+ "test/command_test.rb",
65
+ "test/commands/boot_main_app_test.rb",
66
+ "test/commands/enter_bootloader_test.rb",
67
+ "test/commands/get_device_mode_test.rb",
68
+ "test/commands/get_flash_block_test.rb",
69
+ "test/commands/get_permanent_flags_test.rb",
70
+ "test/commands/get_versions_test.rb",
71
+ "test/commands/is_page_blank_test.rb",
72
+ "test/commands/l1_diagnostics_test.rb",
73
+ "test/commands/l2_diagnostics_test.rb",
74
+ "test/commands/ping_test.rb",
75
+ "test/commands/set_device_mode_test.rb",
76
+ "test/commands/set_permanent_flags_test.rb",
77
+ "test/data/enter_bootloader.txt",
78
+ "test/data/get_device_mode.txt",
79
+ "test/data/get_factory_config.txt",
80
+ "test/data/get_permanent_flags.txt",
81
+ "test/data/get_soul.txt",
82
+ "test/data/get_version.txt",
83
+ "test/data/is_page_blank.txt",
84
+ "test/data/l1_diagnostics.txt",
85
+ "test/data/l2_diagnostics.txt",
86
+ "test/data/ping.txt",
87
+ "test/data/set_device_mode.txt",
88
+ "test/data/set_permanent_flags.txt",
89
+ "test/helper.rb",
90
+ "test/replay_channel_test.rb",
91
+ "test/response_test.rb",
92
+ "test/session_test.rb",
93
+ "test/sphero_pwn_test.rb"
94
+ ]
95
+ s.homepage = "http://github.com/pwnall/sphero_pwn"
96
+ s.licenses = ["MIT"]
97
+ s.rubygems_version = "2.4.5.1"
98
+ s.summary = "Wrapper for Sphero's bluetooth protocol"
99
+
100
+ if s.respond_to? :specification_version then
101
+ s.specification_version = 4
102
+
103
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
104
+ s.add_runtime_dependency(%q<rubyserial>, [">= 0.2.4"])
105
+ s.add_development_dependency(%q<bundler>, [">= 1.3.5"])
106
+ s.add_development_dependency(%q<jeweler>, [">= 2.0.1"])
107
+ s.add_development_dependency(%q<minitest>, [">= 5.8.3"])
108
+ s.add_development_dependency(%q<mocha>, [">= 1.1.0"])
109
+ s.add_development_dependency(%q<simplecov>, [">= 0"])
110
+ s.add_development_dependency(%q<yard>, [">= 0.8.7.6"])
111
+ else
112
+ s.add_dependency(%q<rubyserial>, [">= 0.2.4"])
113
+ s.add_dependency(%q<bundler>, [">= 1.3.5"])
114
+ s.add_dependency(%q<jeweler>, [">= 2.0.1"])
115
+ s.add_dependency(%q<minitest>, [">= 5.8.3"])
116
+ s.add_dependency(%q<mocha>, [">= 1.1.0"])
117
+ s.add_dependency(%q<simplecov>, [">= 0"])
118
+ s.add_dependency(%q<yard>, [">= 0.8.7.6"])
119
+ end
120
+ else
121
+ s.add_dependency(%q<rubyserial>, [">= 0.2.4"])
122
+ s.add_dependency(%q<bundler>, [">= 1.3.5"])
123
+ s.add_dependency(%q<jeweler>, [">= 2.0.1"])
124
+ s.add_dependency(%q<minitest>, [">= 5.8.3"])
125
+ s.add_dependency(%q<mocha>, [">= 1.1.0"])
126
+ s.add_dependency(%q<simplecov>, [">= 0"])
127
+ s.add_dependency(%q<yard>, [">= 0.8.7.6"])
128
+ end
129
+ end
130
+