pebblewatch 0.0.1 → 0.0.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.
data/README.md CHANGED
@@ -6,9 +6,9 @@ The protocol implementation was based on the documentation at http://pebbledev.o
6
6
 
7
7
  ## To Do
8
8
 
9
- - [ ] Basic protocol communication
10
- - [ ] Sending of messages (notifications etc)
11
- - [ ] Receiving of events (log, music control)
9
+ - [x] Basic protocol communication
10
+ - [x] Sending of messages (notifications etc)
11
+ - [x] Receiving of events (log, music control)
12
12
  - [ ] Firmware/app uploading
13
13
  - [ ] CLI
14
14
  - [ ] REPL
@@ -16,17 +16,109 @@ The protocol implementation was based on the documentation at http://pebbledev.o
16
16
  ## Installation
17
17
 
18
18
  ```sh
19
- gem install pebble
19
+ gem install pebblewatch
20
20
  ```
21
21
 
22
22
  ## Usage
23
23
 
24
+ Make sure your Pebble is paired to your computer and set up as a serial port. We're going to need the path or index of the port, which in the case of OS X looks like `/dev/tty.Pebble7F30-SerialPortSe` for Pebble ID `7F30`.
25
+
24
26
  ```ruby
25
27
  require "pebble"
26
28
 
27
- # etc
29
+ # Create your watch using the serial port assigned to your Pebble.
30
+ watch = Pebble::Watch.new("7F30", "/dev/tty.Pebble7F30-SerialPortSe")
31
+ # You can also use autodetection if you're on OS X:
32
+ # watch = Pebble::Watch.autodetect
33
+
34
+ # The watch object will be on the receiving end of 3 kinds of events:
35
+ watch.on_event(:log) do |event|
36
+ puts "LOG"
37
+ puts "timestamp: #{event.timestamp}"
38
+ puts "level: #{event.level}"
39
+ puts "filename: #{event.filename}"
40
+ puts "linenumber: #{event.linenumber}"
41
+ puts "message: #{event.message}"
42
+ end
43
+
44
+ watch.on_event(:system_message) do |event|
45
+ puts "System Message: #{event.message} (#{event.code})"
46
+ end
47
+
48
+ handler = watch.on_event(:media_control) do |event|
49
+ case event.button
50
+ when :playpause
51
+ puts "Play or pause music"
52
+ when :next
53
+ puts "Next track"
54
+ when :previous
55
+ puts "Previous track"
56
+ end
57
+ end
58
+
59
+ # Suddenly got a change of heart? Just cover your ears.
60
+ watch.stop_listening(:media_control, handler)
61
+
62
+ # To make sure we don't miss any events, we haven't connected yet.
63
+ # To be able to *send* stuff to the watch, we will now.
64
+ watch.connect
65
+
66
+
67
+ # We can of course send notifications.
68
+ watch.ping
69
+ watch.notification_sms("Scarlett Johansson", "Hey baby, what are you doing tonight?")
70
+ watch.notification_email("Tim Cook", "RE: Final offer",
71
+ "All right, you drive a hard bargain. We'll pay $2 Billion for the whole shop and that is our final offer.")
72
+
73
+ # Or let Pebble know what we're listening to.
74
+ watch.set_nowplaying_metadata(
75
+ "Artist you've probably never heard of",
76
+ "Album released exclusively through SoundCloud",
77
+ "Song that doesn't suck per se but isn't great either"
78
+ )
79
+
80
+ # We can also do some maintenancy things, although there's currently no firmware/app uploading.
81
+ versions = watch.get_versions
82
+ puts "Normal firmware version: #{versions[:firmwares][:normal][:version]}"
83
+ puts "Recovery firmware version: #{versions[:firmwares][:recovery][:version]}"
84
+ puts "Bluetooth MAC address: #{versions[:btmac]}"
85
+
86
+ # Fun fact: We don't have to wait for the results synchronously. (This works on every message with a response.)
87
+ watch.get_installed_apps do |apps|
88
+ puts "Installed apps: (#{apps[:apps].length} of #{apps[:banks_count]} banks in use)"
89
+ apps[:apps].each do |app|
90
+ puts "#{app[:index]}/#{app[:id]}: #{app[:name]} by #{app[:author]}"
91
+ end
92
+ end
93
+
94
+ # Dieting is not just for dogs anymore.
95
+ watch.remove_app(id, index)
96
+
97
+ time = watch.get_time
98
+ # Or asynchronously: watch.get_time { |time| ... }
99
+
100
+ # Time travel is just one method call away.
101
+ watch.set_time(Time.now + (365 * 24 * 60 * 60))
102
+
103
+ # This is mostly interesting for internal stuff, but since there's a :system_message event as well, I thought why not.
104
+ watch.system_message(Pebble::SystemMessages::FIRMWARE_OUT_OF_DATE)
105
+
106
+ # Yeah, I'd stay away from this one.
107
+ watch.reset
108
+
109
+
110
+ # If you're done sending messages but want the program to keep listing for incoming events, say so:
111
+ watch.listen_for_events
112
+ # Note that listening will only end when the connection is lost, so anything that comes after this call will only then be executed.
113
+ # This will generally be the last call in your program.
114
+
115
+ # If we don't want to wait around and listen, just let the program exit or disconnect explicitly:
116
+ watch.disconnect
28
117
  ```
29
118
 
119
+ ## Examples
120
+ Check out the `examples/` folder for two examples that I actually use myself. They're kind of similar, but should give you an idea of how this whole thing works.
121
+
30
122
  ## License
31
123
  Copyright (c) 2013 Douwe Maan
32
124
 
@@ -16,7 +16,7 @@ module Pebble
16
16
  SMS = 32
17
17
  GPS = 64
18
18
  BTLE = 128
19
- CAMERA_FRONT = 240 # No, that doesn't make sense, but it's apparently true.
19
+ # CAMERA_FRONT = 240 # Doesn't make sense as it'd mess up the bitmask, but it's apparently true.
20
20
  CAMERA_REAR = 256
21
21
  ACCEL = 512
22
22
  GYRO = 1024
@@ -20,5 +20,9 @@ module Pebble
20
20
  RUNKEEPER = 7000
21
21
  PUT_BYTES = 48879
22
22
  MAX_ENDPOINT = 65535
23
+
24
+ def self.for_code(code)
25
+ constants.find { |constant| const_get(constant) == code }
26
+ end
23
27
  end
24
28
  end
@@ -1,5 +1,160 @@
1
+ require "serialport"
2
+
1
3
  module Pebble
2
4
  class Protocol
5
+ module Errors
6
+ class NotConnected < StandardError; end
7
+ class LostConnection < StandardError; end
8
+ class MalformedResponse < StandardError; end
9
+ end
10
+
11
+ attr_reader :connected
12
+ attr_reader :message_handlers
13
+
14
+ def self.open(port)
15
+ protocol = new(port)
16
+
17
+ begin
18
+ protocol.connect
19
+ yield protocol
20
+ ensure
21
+ protocol.disconnect
22
+ end
23
+ nil
24
+ end
25
+
26
+ def initialize(port)
27
+ @port = port
28
+
29
+ @connected = false
30
+ @send_message_mutex = Mutex.new
31
+ @message_handlers = Hash.new { |hash, key| hash[key] = [] }
32
+ end
33
+
34
+ def connect
35
+ puts "Connecting with port #{@port}"
36
+
37
+ @serial_port = SerialPort.new(@port, baudrate: 115200)
38
+ @serial_port.read_timeout = 500
39
+
40
+ @connected = true
41
+ puts "Connected"
42
+
43
+ @receive_messages_thread = Thread.new(&method(:receive_messages))
44
+
45
+ true
46
+ end
47
+
48
+ def disconnect
49
+ raise Errors::NotConnected unless @connected
50
+
51
+ @connected = false
52
+
53
+ @serial_port.close()
54
+ @serial_port = nil
55
+
56
+ true
57
+ end
58
+
59
+ def listen_for_messages
60
+ raise Errors::NotConnected unless @connected
61
+
62
+ @receive_messages_thread.join
63
+ end
64
+
65
+ def on_receive(endpoint = :any, &handler)
66
+ @message_handlers[endpoint] << handler
67
+ handler
68
+ end
69
+
70
+ def stop_receiving(*params)
71
+ handler = params.pop
72
+ endpoint = params.pop || :any
73
+
74
+ @message_handlers[endpoint].delete(handler)
75
+ end
76
+
77
+ def send_message(endpoint, message, async_response_handler = nil, &response_parser)
78
+ raise Errors::NotConnected unless @connected
79
+
80
+ message ||= ""
81
+
82
+ puts "Sending #{Endpoints.for_code(endpoint)}: #{message.inspect}"
83
+
84
+ data = [message.size, endpoint].pack("S>S>") + message
85
+
86
+ @send_message_mutex.synchronize do
87
+ @serial_port.write data
88
+
89
+ if response_parser
90
+ if async_response_handler
91
+ identifier = on_receive(endpoint) do |response_message|
92
+ stop_receiving(endpoint, identifier)
93
+
94
+ parsed_response = response_parser.call(response_message)
95
+
96
+ async_response_handler.call(parsed_response)
97
+ end
98
+
99
+ true
100
+ else
101
+ received = false
102
+ parsed_response = nil
103
+ identifier = on_receive(endpoint) do |response_message|
104
+ stop_receiving(endpoint, identifier)
105
+
106
+ parsed_response = response_parser.call(response_message)
107
+ received = true
108
+ end
109
+
110
+ sleep 0.015 until received
111
+
112
+ parsed_response
113
+ end
114
+ else
115
+ true
116
+ end
117
+ end
118
+ end
119
+
120
+ private
121
+ def receive_messages
122
+ puts "Waiting for messages"
123
+ while @connected
124
+ header = @serial_port.read(4)
125
+ next unless header
126
+
127
+ raise Errors::MalformedResponse if header.length < 4
128
+
129
+ size, endpoint = header.unpack("S>S>")
130
+ message = @serial_port.read(size)
131
+
132
+ puts "Received #{Endpoints.for_code(endpoint)}: #{message.inspect}"
133
+
134
+ trigger_received(endpoint, message)
135
+ end
136
+ rescue IOError => e
137
+ if @connected
138
+ puts "Lost connection"
139
+ @connected = false
140
+ raise Errors::LostConnection
141
+ end
142
+ ensure
143
+ puts "Finished waiting for messages"
144
+ end
145
+
146
+ def trigger_received(endpoint, message)
147
+ @message_handlers[:any].each do |handler|
148
+ Thread.new(handler) do |handler|
149
+ handler.call(endpoint, message)
150
+ end
151
+ end
3
152
 
153
+ @message_handlers[endpoint].each do |handler|
154
+ Thread.new(handler) do |handler|
155
+ handler.call(message)
156
+ end
157
+ end
158
+ end
4
159
  end
5
160
  end
@@ -1,10 +1,16 @@
1
- module SystemMessages
2
- FIRMWARE_AVAILABLE = 0
3
- FIRMWARE_START = 1
4
- FIRMWARE_COMPLETE = 2
5
- FIRMWARE_FAIL = 3
6
- FIRMWARE_UP_TO_DATE = 4
7
- FIRMWARE_OUT_OF_DATE = 5
8
- BLUETOOTH_START_DISCOVERABLE = 6
9
- BLUETOOTH_END_DISCOVERABLE = 7
1
+ module Pebble
2
+ module SystemMessages
3
+ FIRMWARE_AVAILABLE = 0
4
+ FIRMWARE_START = 1
5
+ FIRMWARE_COMPLETE = 2
6
+ FIRMWARE_FAIL = 3
7
+ FIRMWARE_UP_TO_DATE = 4
8
+ FIRMWARE_OUT_OF_DATE = 5
9
+ BLUETOOTH_START_DISCOVERABLE = 6
10
+ BLUETOOTH_END_DISCOVERABLE = 7
11
+
12
+ def self.for_code(code)
13
+ constants.find { |constant| const_get(constant) == code }
14
+ end
15
+ end
10
16
  end
@@ -1,3 +1,3 @@
1
1
  module Pebble
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/pebble/watch.rb CHANGED
@@ -2,6 +2,260 @@ require "pebble/watch/event"
2
2
 
3
3
  module Pebble
4
4
  class Watch
5
+ module Errors
6
+ class NoWatchesFound < StandardError; end
7
+ end
5
8
 
9
+ def self.autodetect
10
+ return nil unless RUBY_PLATFORM =~ /darwin/
11
+
12
+ devices = Dir.glob("/dev/tty.Pebble????-SerialPortSe")
13
+
14
+ raise NoWatchesFound if devices.length == 0
15
+ puts "Found multiple watches" if devices.length > 1
16
+
17
+ port = devices.first
18
+ id = port[15, 4]
19
+ puts "Found watch with ID #{id}"
20
+
21
+ return new(id, port)
22
+ end
23
+
24
+ def self.open(id, port)
25
+ watch = new(id, port)
26
+
27
+ begin
28
+ watch.connect
29
+ yield watch
30
+ ensure
31
+ watch.disconnect
32
+ end
33
+ nil
34
+ end
35
+
36
+ attr_reader :id
37
+ attr_reader :protocol
38
+ attr_reader :event_handlers
39
+ attr_accessor :session_capabilities
40
+ attr_accessor :client_capabilities
41
+
42
+ def initialize(id, port)
43
+ @id = id
44
+
45
+ @protocol = Protocol.new(port)
46
+ @event_handlers = Hash.new { |hash, key| hash[key] = [] }
47
+
48
+ # We're mirroring Android here.
49
+ @session_capabilities = Capabilities::Session::GAMMA_RAY
50
+ @client_capabilities = Capabilities::Client::TELEPHONY | Capabilities::Client::SMS | Capabilities::Client::ANDROID
51
+
52
+ answer_phone_version_message
53
+
54
+ receive_event_messages
55
+ end
56
+
57
+ def connect
58
+ @protocol.connect
59
+ end
60
+
61
+ def disconnect
62
+ @protocol.disconnect
63
+ end
64
+
65
+ def listen_for_events
66
+ @protocol.listen_for_messages
67
+ end
68
+
69
+ def on_event(event = :any, &handler)
70
+ @event_handlers[event] << handler
71
+ handler
72
+ end
73
+
74
+ def stop_listening(*params)
75
+ handler = params.pop
76
+ event = params.pop || :any
77
+
78
+ @event_handlers[event].delete(handler)
79
+ end
80
+
81
+
82
+ def ping(cookie = 0xDEADBEEF, &async_response_handler)
83
+ message = [0, cookie].pack("CL>")
84
+
85
+ @protocol.send_message(Endpoints::PING, message, async_response_handler) do |message|
86
+ restype, cookie = message.unpack("CL>")
87
+ cookie
88
+ end
89
+ end
90
+
91
+ def notification_sms(sender, body)
92
+ notification(:sms, sender, body)
93
+ end
94
+
95
+ def notification_email(sender, subject, body)
96
+ notification(:email, sender, body, subject)
97
+ end
98
+
99
+ def set_nowplaying_metadata(artist, album, track)
100
+ message = [16].pack("C")
101
+ message << package_strings(artist, album, track, 30)
102
+
103
+ @protocol.send_message(Endpoints::MUSIC_CONTROL, message)
104
+ end
105
+
106
+ def get_versions(&async_response_handler)
107
+ @protocol.send_message(Endpoints::VERSION, "\x00", async_response_handler) do |message|
108
+ response = {}
109
+
110
+ response[:firmwares] = {}
111
+
112
+ size = 47
113
+ [:normal, :recovery].each_with_index do |type, index|
114
+ offset = index * size + 1
115
+
116
+ fw = {}
117
+
118
+ fw[:timestamp], fw[:version], fw[:commit], fw[:is_recovery],
119
+ fw[:hardware_platform], fw[:metadata_version] =
120
+ message[offset, size].unpack("L>A32A8ccc")
121
+
122
+ fw[:is_recovery] = (fw[:is_recovery] == 1)
123
+
124
+ response[:firmwares][type] = fw
125
+ end
126
+
127
+ response[:bootloader_timestamp], response[:hardware_version], response[:serial] =
128
+ message[95, 25].unpack("L>A9A12")
129
+
130
+ response[:btmac] = message[120, 6].unpack("H*").first.scan(/../).reverse.map { |c| c.upcase }.join(":")
131
+
132
+ response
133
+ end
134
+ end
135
+
136
+ def get_installed_apps(&async_response_handler)
137
+ @protocol.send_message(Endpoints::APP_INSTALL_MANAGER, "\x01", async_response_handler) do |message|
138
+ response = {}
139
+
140
+ response[:banks_count], apps_count = message[1, 8].unpack("L>L>")
141
+ response[:apps] = []
142
+
143
+ size = 78
144
+ apps_count.times do |index|
145
+ offset = index * size + 9
146
+
147
+ app = {}
148
+
149
+ app[:id], app[:index], app[:name], app[:author], app[:flags], app[:version] =
150
+ message[offset, size].unpack("L>L>A32A32L>S>")
151
+
152
+ response[:apps] << app
153
+ end
154
+
155
+ response
156
+ end
157
+ end
158
+
159
+ def remove_app(app_id, app_index)
160
+ message = [2, app_id, app_index].pack("cL>L>")
161
+
162
+ @protocol.send_message(Endpoints::APP_INSTALL_MANAGER, message)
163
+ end
164
+
165
+ def get_time(&async_response_handler)
166
+ @protocol.send_message(Endpoints::TIME, "\x00", async_response_handler) do |message|
167
+ restype, timestamp = message.unpack("CL>")
168
+ Time.at(timestamp)
169
+ end
170
+ end
171
+
172
+ def set_time(time)
173
+ timestamp = time.to_i
174
+ message = [2, timestamp].pack("CL>")
175
+
176
+ @protocol.send_message(Endpoints::TIME, message)
177
+ end
178
+
179
+ def system_message(code)
180
+ puts "Sending system message #{SystemMessages.for_code(code)}"
181
+
182
+ message = [code].pack("S>")
183
+
184
+ @protocol.send_message(Endpoints::SYSTEM_MESSAGE, message)
185
+ end
186
+
187
+ def reset
188
+ @protocol.send_message(Endpoints::RESET, "\x00")
189
+ end
190
+
191
+ private
192
+ def answer_phone_version_message
193
+ @protocol.on_receive(Endpoints::PHONE_VERSION) do |message|
194
+ response_message = [1, -1].pack("Cl>")
195
+ response_message << [@session_capabilities, @client_capabilities].pack("L>L>")
196
+
197
+ @protocol.send_message(Endpoints::PHONE_VERSION, response_message)
198
+ end
199
+ end
200
+
201
+ def receive_event_messages
202
+ events = [
203
+ [:log, Endpoints::LOGS, LogEvent],
204
+ [:system_message, Endpoints::SYSTEM_MESSAGE, SystemMessageEvent],
205
+ [:media_control, Endpoints::MUSIC_CONTROL, MediaControlEvent]
206
+ ]
207
+
208
+ events.each do |(name, endpoint, event_klass)|
209
+ @protocol.on_receive(endpoint) do |message|
210
+ event = event_klass.parse(message)
211
+ trigger_event(name, event) if event
212
+ end
213
+ end
214
+ end
215
+
216
+ def trigger_event(name, event)
217
+ puts "Event '#{name}': #{event.inspect}"
218
+
219
+ @event_handlers[:any].each do |handler|
220
+ Thread.new(handler) do |handler|
221
+ handler.call(name, event)
222
+ end
223
+ end
224
+
225
+ @event_handlers[name].each do |handler|
226
+ Thread.new(handler) do |handler|
227
+ handler.call(event)
228
+ end
229
+ end
230
+ end
231
+
232
+ def notification(type, *params)
233
+ types = {
234
+ email: 0,
235
+ sms: 1
236
+ }
237
+
238
+ timestamp = Time.now.to_i
239
+ params.insert(2, timestamp.to_s)
240
+
241
+ message = [types[type]].pack("C")
242
+ message << package_strings(*params)
243
+
244
+ @protocol.send_message(Endpoints::NOTIFICATION, message)
245
+ end
246
+
247
+ def package_strings(*parts)
248
+ max_part_length = 255
249
+ max_part_length = parts.pop if parts.last.is_a?(Integer)
250
+
251
+ message = ""
252
+ parts.each do |part|
253
+ part ||= ""
254
+
255
+ part = part[0, max_part_length] if part.length > max_part_length
256
+ message << [part.length].pack("C") + part
257
+ end
258
+ message
259
+ end
6
260
  end
7
261
  end
@@ -7,4 +7,5 @@ module Pebble
7
7
  end
8
8
 
9
9
  require "pebble/watch/log_event"
10
- require "pebble/watch/media_control_event"
10
+ require "pebble/watch/system_message_event"
11
+ require "pebble/watch/media_control_event"
@@ -1,7 +1,41 @@
1
1
  module Pebble
2
2
  class Watch
3
3
  class LogEvent < Event
4
+ attr_accessor :timestamp
5
+ attr_accessor :level
6
+ attr_accessor :filename
7
+ attr_accessor :linenumber
8
+ attr_accessor :message
4
9
 
10
+ def self.parse(raw_message)
11
+ return nil if raw_message.length < 8
12
+
13
+ timestamp, level, message_size, linenumber = raw_message[0, 8].unpack("L>CCS>")
14
+ filename = raw_message[8, 16]
15
+ message = raw_message[24, message_size]
16
+
17
+ log_levels = {
18
+ 1 => :error,
19
+ 50 => :warning,
20
+ 100 => :info,
21
+ 200 => :debug,
22
+ 250 => :verbose
23
+ }
24
+
25
+ event = new
26
+
27
+ event.timestamp = Time.at(timestamp)
28
+ event.level = log_levels[level] || :unknown
29
+ event.linenumber = linenumber
30
+ event.filename = filename
31
+ event.message = message
32
+
33
+ event
34
+ end
35
+
36
+ def inspect
37
+ "[#{self.level.to_s.capitalize}] #{self.message}"
38
+ end
5
39
  end
6
40
  end
7
41
  end
@@ -1,7 +1,29 @@
1
1
  module Pebble
2
2
  class Watch
3
3
  class MediaControlEvent < Event
4
+ attr_accessor :button
4
5
 
6
+ BUTTONS = {
7
+ 1 => :playpause,
8
+ 4 => :next,
9
+ 5 => :previous
10
+ }
11
+
12
+ def self.parse(message)
13
+ button_id = message.unpack("C").first
14
+
15
+ return nil unless BUTTONS.has_key?(button_id)
16
+
17
+ event = new
18
+
19
+ event.button = BUTTONS[button_id]
20
+
21
+ event
22
+ end
23
+
24
+ def inspect
25
+ self.button.to_s.capitalize
26
+ end
5
27
  end
6
28
  end
7
29
  end
@@ -0,0 +1,23 @@
1
+ module Pebble
2
+ class Watch
3
+ class SystemMessageEvent < Event
4
+ attr_accessor :code
5
+
6
+ def self.parse(message)
7
+ event = new
8
+
9
+ event.code = message.length == 2 ? message.unpack("S>").first : -1
10
+
11
+ event
12
+ end
13
+
14
+ def message
15
+ SystemMessages.for_code(self.code) || "Unknown"
16
+ end
17
+
18
+ def inspect
19
+ "#{self.message} (#{self.code})"
20
+ end
21
+ end
22
+ end
23
+ end
File without changes
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pebblewatch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-30 00:00:00.000000000 Z
12
+ date: 2013-04-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: serialport
16
- requirement: &70167654887920 !ruby/object:Gem::Requirement
16
+ requirement: &70119208390380 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '1.1'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70167654887920
24
+ version_requirements: *70119208390380
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &70167654886380 !ruby/object:Gem::Requirement
27
+ requirement: &70119208388900 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70167654886380
35
+ version_requirements: *70119208388900
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &70167654884180 !ruby/object:Gem::Requirement
38
+ requirement: &70119208388220 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70167654884180
46
+ version_requirements: *70119208388220
47
47
  description: A Ruby library for communicating with your Pebble smartwatch.
48
48
  email: douwe@selenight.nl
49
49
  executables: []
@@ -52,15 +52,16 @@ extra_rdoc_files: []
52
52
  files:
53
53
  - lib/pebble/capabilities.rb
54
54
  - lib/pebble/endpoints.rb
55
- - lib/pebble/pebblewatch.rb
56
55
  - lib/pebble/protocol.rb
57
56
  - lib/pebble/system_messages.rb
58
57
  - lib/pebble/version.rb
59
58
  - lib/pebble/watch/event.rb
60
59
  - lib/pebble/watch/log_event.rb
61
60
  - lib/pebble/watch/media_control_event.rb
61
+ - lib/pebble/watch/system_message_event.rb
62
62
  - lib/pebble/watch.rb
63
63
  - lib/pebble.rb
64
+ - lib/pebblewatch.rb
64
65
  - LICENSE
65
66
  - README.md
66
67
  - Rakefile
@@ -80,7 +81,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
80
81
  version: '0'
81
82
  segments:
82
83
  - 0
83
- hash: -921215416686416303
84
+ hash: 4433695980679618883
84
85
  required_rubygems_version: !ruby/object:Gem::Requirement
85
86
  none: false
86
87
  requirements:
@@ -89,7 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
90
  version: '0'
90
91
  segments:
91
92
  - 0
92
- hash: -921215416686416303
93
+ hash: 4433695980679618883
93
94
  requirements: []
94
95
  rubyforge_project:
95
96
  rubygems_version: 1.8.6