particlerb 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +210 -32
- data/lib/particle/client/devices.rb +7 -6
- data/lib/particle/client/publish.rb +38 -0
- data/lib/particle/client/tokens.rb +101 -0
- data/lib/particle/client/webhooks.rb +66 -0
- data/lib/particle/client.rb +12 -2
- data/lib/particle/configurable.rb +1 -1
- data/lib/particle/connection.rb +16 -11
- data/lib/particle/device.rb +10 -17
- data/lib/particle/error.rb +2 -0
- data/lib/particle/event.rb +10 -0
- data/lib/particle/model.rb +49 -0
- data/lib/particle/token.rb +76 -0
- data/lib/particle/version.rb +1 -1
- data/lib/particle/webhook.rb +62 -0
- data/lib/particle.rb +19 -0
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3df82a4b9e831fa635c52d24e9d0f59bd3b6209d
|
4
|
+
data.tar.gz: 61c6aa2eba790faeee99e4cde8ea6f04e214946b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a4f275a4d1b7b1dde368e620a76205e4ee34fd23aaca4682d3df0bee6c3407786504d05b648cfe56ddb8ea0e88404a69d5a3479705e58ee6e9eb2276cb5f1d5
|
7
|
+
data.tar.gz: 95e421509a8109498915efcb25096969204afb0fb673a38edca51957ea20874d784a9dca23383e5d6cce600bb65e429f0ad7b8048b8de06985eb09b7c8d6d32f
|
data/README.md
CHANGED
@@ -1,12 +1,16 @@
|
|
1
|
-
|
1
|
+
## particlerb ##
|
2
2
|
|
3
|
-
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/particlerb.svg)](http://badge.fury.io/rb/particlerb)
|
4
|
+
[![Build Status](https://travis-ci.org/monkbroc/particlerb.svg)](https://travis-ci.org/monkbroc/particlerb)
|
5
|
+
[![Code Climate](https://codeclimate.com/github/monkbroc/particlerb/badges/gpa.svg)](https://codeclimate.com/github/monkbroc/particlerb)
|
6
|
+
|
7
|
+
Ruby client for the [Particle.io] Cloud API with an object-oriented interface
|
4
8
|
|
5
9
|
[Particle.io]: https://www.particle.io
|
6
10
|
|
7
11
|
*Note: this is not an official gem by Particle. It is maintained by Julien Vanier.*
|
8
12
|
|
9
|
-
##
|
13
|
+
## Installation
|
10
14
|
|
11
15
|
Install via Rubygems
|
12
16
|
|
@@ -14,82 +18,111 @@ Install via Rubygems
|
|
14
18
|
|
15
19
|
... or add to your Gemfile
|
16
20
|
|
17
|
-
gem "particlerb", "~> 0.0.
|
21
|
+
gem "particlerb", "~> 0.0.2"
|
18
22
|
|
19
23
|
|
20
|
-
###
|
24
|
+
### Providing credentials
|
21
25
|
|
22
|
-
API
|
23
|
-
configuration) or as client instance methods.
|
26
|
+
**A Particle cloud API access token is necessary for most requests.** You can use the one from the [Web IDE][] for testing, but it's recommended to generate a new token with this gem using `Particle.login` or with the [Particle CLI][] using `particle token new`
|
24
27
|
|
25
28
|
```ruby
|
26
|
-
# Provide
|
29
|
+
# Provide acess token as an environment variable
|
30
|
+
ENV['PARTICLE_ACCESS_TOKEN']
|
31
|
+
|
32
|
+
# Or configure global authentication credentials
|
33
|
+
# If you use Rails, you can put this in config/initializers/particle.rb
|
27
34
|
Particle.configure do |c|
|
28
35
|
c.access_token = "38bb7b318cc6898c80317decb34525844bc9db55"
|
29
36
|
end
|
30
37
|
|
31
|
-
#
|
32
|
-
Particle.
|
38
|
+
# Or pass access token when creating a client
|
39
|
+
# If no token is passed to Particle::Client.new, the global or environment one is used
|
40
|
+
client = Particle::Client.new(access_token: "38bb7b318cc6898c80317decb34525844bc9db55")
|
33
41
|
```
|
34
|
-
|
42
|
+
|
43
|
+
### Making requests
|
44
|
+
|
45
|
+
API methods are available as module methods (consuming module-level
|
46
|
+
configuration) or as client instance methods.
|
35
47
|
|
36
48
|
```ruby
|
37
|
-
#
|
38
|
-
|
49
|
+
# Fetch the list of devices using the global client
|
50
|
+
Particle.devices
|
51
|
+
|
52
|
+
# Or used a newly created client
|
53
|
+
client = Particle::Client.new
|
39
54
|
# Fetch the list of devices
|
40
55
|
client.devices
|
41
56
|
```
|
42
57
|
|
43
|
-
|
58
|
+
[Web IDE]: http://docs.particle.io/core/build/#flash-apps-with-particle-build-account-information
|
59
|
+
[Particle CLI]: http://docs.particle.io/core/cli
|
60
|
+
|
61
|
+
## Interacting with devices
|
44
62
|
|
45
|
-
|
63
|
+
List all devices. Rreturns an `Array` of `Particle::Device`.
|
46
64
|
|
47
|
-
List all devices (returns an `Array` of `Device`)
|
48
65
|
```ruby
|
49
66
|
devices = Particle.devices
|
50
67
|
```
|
51
68
|
|
52
|
-
Get a `Device` by id or name
|
69
|
+
Get a `Particle::Device` by id or name.
|
70
|
+
|
53
71
|
```ruby
|
54
72
|
device = Particle.device('blue_fire')
|
55
73
|
device = Particle.device('f8bbe1e6e69e05c9c405ba1ca504d438061f1b0d')
|
56
74
|
```
|
57
75
|
|
58
|
-
Get information about a device
|
76
|
+
Get information about a device.
|
77
|
+
|
59
78
|
```ruby
|
60
79
|
device = Particle.device('blue_fire')
|
61
|
-
device.id
|
62
|
-
device.name
|
63
|
-
device.connected?
|
64
|
-
device.variables
|
65
|
-
device.functions
|
66
80
|
device.attributes # Hash of all attributes
|
81
|
+
device.id # ==> 'f8bbe1e6e69e05c9c405ba1ca504d438061f1b0d'
|
82
|
+
device.name # ==> 'blue_fire'
|
83
|
+
device.connected? # true
|
84
|
+
device.variables # {:myvar => "double" } or nil if not connected
|
85
|
+
device.functions # ["myfunction"] or nil if not connected
|
67
86
|
device.get_attributes # forces refresh of all attributes from the cloud
|
87
|
+
|
88
|
+
# If you get a Device from the Particle.devices call, you will need to call
|
89
|
+
# get_attributes to get the list of functions and variables since that's not
|
90
|
+
# returned by the cloud when calling Particle.devices
|
91
|
+
device = Particle.devices.first
|
92
|
+
device.connected? # ==> true
|
93
|
+
device.functions # ==> nil
|
94
|
+
device.get_attributes
|
95
|
+
device.functions # ==> ["myfunction"]
|
68
96
|
```
|
69
97
|
|
70
|
-
Claim a device and add it to your account
|
98
|
+
Claim a device by id and add it to your account. Returns a `Particle::Device`.
|
99
|
+
|
71
100
|
```ruby
|
72
|
-
Particle.device('
|
101
|
+
Particle.device('f8bbe1e6e69e05c9c405ba1ca504d438061f1b0d').claim
|
73
102
|
```
|
74
103
|
|
75
|
-
Remove a device from your account
|
104
|
+
Remove a device from your account. Returns true on success.
|
105
|
+
|
76
106
|
```ruby
|
77
107
|
Particle.device('blue_fire').remove
|
78
108
|
Particle.devices.first.remove
|
79
109
|
```
|
80
110
|
|
81
|
-
Rename a device
|
111
|
+
Rename a device. Returns true on success.
|
112
|
+
|
82
113
|
```ruby
|
83
114
|
Particle.device('red').rename('green')
|
84
115
|
```
|
85
116
|
|
86
|
-
Call a function on the firmware
|
117
|
+
Call a function on the firmware with an optional `String` argument. Returns the result of running the function as as `Number`.
|
118
|
+
|
87
119
|
```ruby
|
88
|
-
Particle.device('coffeemaker').function('brew')
|
120
|
+
Particle.device('coffeemaker').function('brew')
|
89
121
|
Particle.devices.first.function('digitalWrite', '1')
|
90
122
|
```
|
91
123
|
|
92
|
-
Get the value of a firmware variable
|
124
|
+
Get the value of a firmware variable. Returns the result as a `String` or `Number`.
|
125
|
+
|
93
126
|
```ruby
|
94
127
|
Particle.device('mycar').variable('battery') # ==> 12.33
|
95
128
|
device = Particle.device('f8bbe1e6e69e05c9c405ba1ca504d438061f1b0d')
|
@@ -97,13 +130,145 @@ device.variable('version') # ==> "1.0.1"
|
|
97
130
|
```
|
98
131
|
|
99
132
|
|
100
|
-
Signal a device to start blinking the RGB LED in rainbow patterns.
|
133
|
+
Signal a device to start blinking the RGB LED in rainbow patterns. Returns whether the device is signaling.
|
134
|
+
|
101
135
|
```ruby
|
102
136
|
Particle.device('nyan_cat').signal(true)
|
103
137
|
```
|
138
|
+
See the [Particle Cloud API documentation about devices][device docs] for more details.
|
139
|
+
|
140
|
+
[device docs]: http://docs.particle.io/core/api/#introduction-device-information
|
141
|
+
|
142
|
+
## Interacting with events
|
143
|
+
|
144
|
+
Publish an event to your devices. Returns true on success.
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
Particle.publish(name: "wakeup")
|
148
|
+
Particle.publish(name: "server_ip", data: "8.8.8.8", ttl: 3600, private: true)
|
149
|
+
```
|
150
|
+
|
151
|
+
Data, ttl and private are optional.
|
152
|
+
|
153
|
+
Data is converted to JSON if it is a Hash or an Array, otherwise it is converted to a String.
|
154
|
+
|
155
|
+
See the [Particle Cloud API documentation about publishing events][publish docs] for more details.
|
156
|
+
|
157
|
+
[publish docs]: http://docs.particle.io/core/api/#publishing-events
|
158
|
+
|
159
|
+
### Limitation: Subscribe not supported
|
160
|
+
|
161
|
+
This gem does not support subscribing (listening) to events from devices.
|
162
|
+
|
163
|
+
This would require an HTTP client that supports streaming responses which is not common in Ruby. Some clients like EM-HTTP-Request do support streaming responses, but are tied to specific architectures like EventMachine.
|
164
|
+
|
165
|
+
For web server applications, webhooks are better suited to process incoming events.
|
166
|
+
|
167
|
+
## Interacting with webhooks
|
168
|
+
|
169
|
+
List existing webhooks. Returns an `Array` of `Particle::Webhook`
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
Particle.webhooks
|
173
|
+
```
|
174
|
+
|
175
|
+
Get info about an existing webhook by id. Returns a `Particle::Webhook`
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
webhook = Particle.webhook('ffcddbd30b860ea3cadd22db')
|
179
|
+
webhook.attributes
|
180
|
+
webhook.event
|
181
|
+
webhook.url
|
182
|
+
```
|
183
|
+
|
184
|
+
Calling `attributes` will also send a test message to your webhook url and report the `response` or `error`.
|
185
|
+
|
186
|
+
```ruby
|
187
|
+
webhook.response
|
188
|
+
webhook.error
|
189
|
+
|
190
|
+
webhook = Particle.webhooks.first
|
191
|
+
webhook.get_attributes # force reloading attributes from the cloud
|
192
|
+
# get_attributes necessary to get the response when Webhook was returned from the
|
193
|
+
# Particle.webhooks() method as it doesn't do a test message on each webhook
|
194
|
+
webhook.response
|
195
|
+
```
|
196
|
+
|
197
|
+
Create a new webhook. Pass a hash of [any options accepted by the Particle Cloud API][webhook options]. Returns a `Particle::Webhook`
|
198
|
+
|
199
|
+
```ruby
|
200
|
+
Particle.webhook(event: "weather", url: "http://myserver.com/report").create
|
201
|
+
```
|
202
|
+
See the [Particle Cloud API documentation about webhooks][webhook docs] for more details.
|
203
|
+
|
204
|
+
[webhook docs]: http://docs.particle.io/core/webhooks/
|
205
|
+
[webhook options]: http://docs.particle.io/core/webhooks/#webhook-options
|
206
|
+
|
207
|
+
## Authentication
|
208
|
+
|
209
|
+
Replace the access token on a client
|
210
|
+
|
211
|
+
```ruby
|
212
|
+
Particle.access_token = 'f1d52ea0de921fad300027763d8c5ebd03b1934d'
|
213
|
+
|
214
|
+
# On client instance
|
215
|
+
client = Particle::Client.new
|
216
|
+
client.access_token = 'f1d52ea0de921fad300027763d8c5ebd03b1934d'
|
217
|
+
```
|
218
|
+
|
219
|
+
**All these following methods requires the account username (email) and password.**
|
220
|
+
|
221
|
+
List all tokens that can be used to access an account. Returns an `Array` of `Particle::Token`
|
222
|
+
|
223
|
+
```ruby
|
224
|
+
Particle.tokens("me@example.com", "pa$$w0rd")
|
225
|
+
```
|
226
|
+
|
227
|
+
Log in and create a new token. Returns a `Particle::Token`. This will also set the token on the client for future calls.
|
228
|
+
|
229
|
+
```ruby
|
230
|
+
Particle.login("me@example.com", "pa$$w0rd")
|
231
|
+
```
|
232
|
+
|
233
|
+
Create a token but don't set it on the client. Returns a `Particle::Token`
|
234
|
+
|
235
|
+
```ruby
|
236
|
+
Particle.token.create("me@example.com", "pa$$w0rd")
|
237
|
+
```
|
238
|
+
|
239
|
+
Invalidate and delete a token. Returns true on success.
|
240
|
+
|
241
|
+
```ruby
|
242
|
+
Particle.token('f1d52ea0de921fad300027763d8c5ebd03b1934d').remove("me@example.com", "pa$$w0rd")
|
243
|
+
Particle.tokens.first.remove("me@example.com", "pa$$w0rd")
|
244
|
+
```
|
245
|
+
|
246
|
+
See the [Particle Cloud API documentation about authentication and token][authentication docs] for more details.
|
247
|
+
|
248
|
+
[authentication docs]: http://docs.particle.io/core/api/#introduction-authentication
|
249
|
+
|
104
250
|
|
251
|
+
## Errors
|
105
252
|
|
106
|
-
|
253
|
+
When any API error occurs, a subclass of `Particle::Error` will be raised.
|
254
|
+
|
255
|
+
The actual error classes are
|
256
|
+
|
257
|
+
- `MissingTokenError`
|
258
|
+
- `BadRequest`
|
259
|
+
- `Unauthorized`
|
260
|
+
- `Forbidden`
|
261
|
+
- `NotFound`
|
262
|
+
- `TimedOut`
|
263
|
+
- `ServerError`
|
264
|
+
|
265
|
+
See [a description of each error on the Particle API docs][error docs].
|
266
|
+
|
267
|
+
[error docs]: http://docs.particle.io/core/api/#introduction-errors
|
268
|
+
|
269
|
+
## Advanced
|
270
|
+
|
271
|
+
All API endpoints are availble directly on the client object as method calls like `Particle.claim_device(id)` but the preferred usage is to call methods on domain objects like `Particle.device(id).claim`. See the various `Particle::Client` subclasses for more details.
|
107
272
|
|
108
273
|
### Accessing HTTP responses
|
109
274
|
|
@@ -117,6 +282,19 @@ response = Particle.last_response
|
|
117
282
|
headers = response.headers
|
118
283
|
```
|
119
284
|
|
285
|
+
## Support
|
286
|
+
|
287
|
+
Open a [GitHub issue][] if you find a bug.
|
288
|
+
|
289
|
+
[Join the conversion on the awesome Particle community forums][forum] to discuss any other topic!
|
290
|
+
|
291
|
+
[GitHub issue]: https://github.com/monkbroc/particlerb/issues
|
292
|
+
[forum]: http://community.particle.io/
|
293
|
+
|
294
|
+
## Versioning
|
295
|
+
|
296
|
+
particlerb follows the [Semantic Versioning](http://semver.org/) standard.
|
297
|
+
|
120
298
|
## Thanks
|
121
299
|
|
122
300
|
This gem is heavily inspired by [Octokit][] by GitHub. I stand on the shoulder of giants. Thanks!
|
@@ -1,13 +1,16 @@
|
|
1
|
+
require 'particle/device'
|
2
|
+
|
1
3
|
module Particle
|
2
4
|
class Client
|
3
5
|
|
4
|
-
#
|
6
|
+
# Client methods for the Particle device API
|
7
|
+
#
|
5
8
|
# @see http://docs.particle.io/core/api/#introduction-list-devices
|
6
9
|
module Devices
|
7
10
|
|
8
11
|
# Create a domain model for a Particle device
|
9
12
|
#
|
10
|
-
# @param target [String, Sawyer::Resource, Device] A device id, name or {Device} object
|
13
|
+
# @param target [String, Sawyer::Resource, Device] A device id, name, Sawyer::Resource or {Device} object
|
11
14
|
# @return [Device] A device object to interact with
|
12
15
|
def device(target)
|
13
16
|
if target.is_a? Device
|
@@ -15,18 +18,16 @@ module Particle
|
|
15
18
|
elsif target.respond_to?(:to_attrs)
|
16
19
|
Device.new(self, target.to_attrs)
|
17
20
|
else
|
18
|
-
Device.new(self, target
|
21
|
+
Device.new(self, target)
|
19
22
|
end
|
20
23
|
end
|
21
24
|
|
22
25
|
# List all Particle devices on the account
|
23
26
|
#
|
24
|
-
# @see http://docs.particle.io/core/api/#introduction-list-devices
|
25
|
-
#
|
26
27
|
# @return [Array<Device>] List of Particle devices to interact with
|
27
28
|
def devices
|
28
29
|
get(Device.list_path).map do |resource|
|
29
|
-
|
30
|
+
device(resource)
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'particle/event'
|
2
|
+
|
3
|
+
module Particle
|
4
|
+
class Client
|
5
|
+
|
6
|
+
# Client methods for the Particle publish API
|
7
|
+
#
|
8
|
+
# @see http://docs.particle.io/core/api/#publishing-events
|
9
|
+
module Publish
|
10
|
+
|
11
|
+
# Publish an event to your devices
|
12
|
+
#
|
13
|
+
# @param options [Hash]
|
14
|
+
# * :name [String]: name of the event to publish
|
15
|
+
# * :data [String,Hash]: optional data to include with the event
|
16
|
+
# * :ttl [Number]: optional Time-To-Live in seconds for the
|
17
|
+
# event (currently ignored by the cloud)
|
18
|
+
# * :private [boolean]: optional key to indicate event should
|
19
|
+
# be published only to your own devices
|
20
|
+
# @return [boolean] true for success
|
21
|
+
def publish(options)
|
22
|
+
params = {
|
23
|
+
name: options.fetch(:name)
|
24
|
+
}
|
25
|
+
case options[:data]
|
26
|
+
when Hash, Array then params[:data] = options[:data].to_json
|
27
|
+
else params[:data] = options[:data].to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
params[:ttl] = options[:ttl] if options[:ttl]
|
31
|
+
params[:private] = true if options[:private]
|
32
|
+
|
33
|
+
result = post(Event.publish_path, params)
|
34
|
+
result.ok
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'particle/token'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Particle
|
5
|
+
class Client
|
6
|
+
|
7
|
+
# Client methods for the Particle authentication/token API
|
8
|
+
#
|
9
|
+
# @see http://docs.particle.io/core/api/#introduction-authentication
|
10
|
+
module Tokens
|
11
|
+
|
12
|
+
# Create a domain model for a Particle token
|
13
|
+
#
|
14
|
+
# @param target [String, Sawyer::Resource, Token] A token id, Sawyer::Resource or {Token} object
|
15
|
+
# @return [Token] A token object to interact with
|
16
|
+
def token(target = {})
|
17
|
+
if target.is_a? Token
|
18
|
+
target
|
19
|
+
elsif target.respond_to?(:to_attrs)
|
20
|
+
Token.new(self, target.to_attrs)
|
21
|
+
else
|
22
|
+
Token.new(self, target)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# List all Particle tokens for the account
|
27
|
+
#
|
28
|
+
# @param username [String] The username (email) used to log in to
|
29
|
+
# the Particle Cloud API
|
30
|
+
# @param password [String] The password used to log in to
|
31
|
+
# the Particle Cloud API
|
32
|
+
# @return [Array<Token>] List of Particle tokens
|
33
|
+
def tokens(username, password)
|
34
|
+
http_options = {
|
35
|
+
username: username,
|
36
|
+
password: password
|
37
|
+
}
|
38
|
+
request(:get, Token.list_path, "", http_options).map do |resource|
|
39
|
+
token(resource)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Authenticate with Particle and create a new token
|
44
|
+
#
|
45
|
+
# @param username [String] The username (email) used to log in to
|
46
|
+
# the Particle Cloud API
|
47
|
+
# @param password [String] The password used to log in to
|
48
|
+
# the Particle Cloud API
|
49
|
+
# @param options [Hash] Additional Particle Cloud API options to
|
50
|
+
# create the token.
|
51
|
+
# @return [Token] The token object
|
52
|
+
def create_token(username, password, options = {})
|
53
|
+
data = URI.encode_www_form({
|
54
|
+
grant_type: 'password', # specified by docs
|
55
|
+
username: username,
|
56
|
+
password: password
|
57
|
+
}.merge(options))
|
58
|
+
http_options = {
|
59
|
+
headers: { content_type: "application/x-www-form-urlencoded" },
|
60
|
+
username: 'particle', # specified by docs
|
61
|
+
password: 'particle' # specified by docs
|
62
|
+
}
|
63
|
+
result = request(:post, Token.create_path, data, http_options)
|
64
|
+
token(result.access_token)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Authenticate with Particle and start using the token on the
|
68
|
+
# client right away
|
69
|
+
#
|
70
|
+
# @param username [String] The username (email) used to log in to
|
71
|
+
# the Particle Cloud API
|
72
|
+
# @param password [String] The password used to log in to
|
73
|
+
# the Particle Cloud API
|
74
|
+
# @param options [Hash] Additional Particle Cloud API options to
|
75
|
+
# create the token.
|
76
|
+
# @return [Token] The token object
|
77
|
+
def login(username, password, options = {})
|
78
|
+
token = create_token(username, password, options)
|
79
|
+
self.access_token = token
|
80
|
+
token
|
81
|
+
end
|
82
|
+
|
83
|
+
# Remove a Particle token
|
84
|
+
#
|
85
|
+
# @param username [String] The username (email) used to log in to
|
86
|
+
# the Particle Cloud API
|
87
|
+
# @param password [String] The password used to log in to
|
88
|
+
# the Particle Cloud API
|
89
|
+
# @param target [String, Token] An token id or {Token} object
|
90
|
+
# @return [boolean] true for success
|
91
|
+
def remove_token(username, password, target)
|
92
|
+
http_options = {
|
93
|
+
username: username,
|
94
|
+
password: password
|
95
|
+
}
|
96
|
+
result = request(:delete, token(target).path, "", http_options)
|
97
|
+
result.ok
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'particle/webhook'
|
2
|
+
|
3
|
+
module Particle
|
4
|
+
class Client
|
5
|
+
|
6
|
+
# Client methods for the Particle webhook API
|
7
|
+
#
|
8
|
+
# @see http://docs.particle.io/core/webhooks/
|
9
|
+
module Webhooks
|
10
|
+
|
11
|
+
# Create a domain model for a Particle webhook
|
12
|
+
#
|
13
|
+
# @param target [String, Sawyer::Resource, Webhook] A webhook id, Sawyer::Resource or {Device} object
|
14
|
+
# @return [Webhook] A webhook object to interact with
|
15
|
+
def webhook(target)
|
16
|
+
if target.is_a? Webhook
|
17
|
+
target
|
18
|
+
elsif target.respond_to?(:to_attrs)
|
19
|
+
Webhook.new(self, target.to_attrs)
|
20
|
+
else
|
21
|
+
Webhook.new(self, target)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# List all Particle webhooks on the account
|
26
|
+
#
|
27
|
+
# @return [Array<Webhook>] List of Particle webhooks to interact with
|
28
|
+
def webhooks
|
29
|
+
get(Webhook.list_path).map do |resource|
|
30
|
+
webhook(resource)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Get information about a Particle webhook
|
35
|
+
#
|
36
|
+
# The Particle cloud will send a test message to the webhook URL
|
37
|
+
# when this is called
|
38
|
+
#
|
39
|
+
# @param target [String, Webhook] A webhook id or {Webhook} object
|
40
|
+
# @return [Hash] The webhook attributes and test message response
|
41
|
+
def webhook_attributes(target)
|
42
|
+
result = get(webhook(target).path)
|
43
|
+
result.to_attrs
|
44
|
+
end
|
45
|
+
|
46
|
+
# Creates a new Particle webhook
|
47
|
+
#
|
48
|
+
# @param options [Hash] Options to configure the webhook
|
49
|
+
# @return [Webhook] The webhook object
|
50
|
+
# @see http://docs.particle.io/core/webhooks/#Webhook-options
|
51
|
+
def create_webhook(options)
|
52
|
+
result = post(Webhook.create_path, options)
|
53
|
+
webhook(result)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Remove a Particle webhook
|
57
|
+
#
|
58
|
+
# @param target [String, Webhook] A webhook id or {Webhook} object
|
59
|
+
# @return [boolean] true for success
|
60
|
+
def remove_webhook(target)
|
61
|
+
result = delete(webhook(target).path)
|
62
|
+
result.ok
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/particle/client.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'particle/connection'
|
2
|
-
require 'particle/device'
|
3
2
|
require 'particle/client/devices'
|
3
|
+
require 'particle/client/publish'
|
4
|
+
require 'particle/client/webhooks'
|
5
|
+
require 'particle/client/tokens'
|
4
6
|
|
5
7
|
module Particle
|
6
8
|
|
@@ -11,6 +13,9 @@ module Particle
|
|
11
13
|
include Particle::Configurable
|
12
14
|
include Particle::Connection
|
13
15
|
include Particle::Client::Devices
|
16
|
+
include Particle::Client::Publish
|
17
|
+
include Particle::Client::Webhooks
|
18
|
+
include Particle::Client::Tokens
|
14
19
|
|
15
20
|
def initialize(options = {})
|
16
21
|
# Use options passed in, but fall back to module defaults
|
@@ -38,7 +43,12 @@ module Particle
|
|
38
43
|
# @param value [String] 40 character Particle OAuth2 access token
|
39
44
|
def access_token=(value)
|
40
45
|
reset_agent
|
41
|
-
@access_token =
|
46
|
+
@access_token =
|
47
|
+
if value.respond_to? :access_token
|
48
|
+
value.access_token
|
49
|
+
else
|
50
|
+
value
|
51
|
+
end
|
42
52
|
end
|
43
53
|
end
|
44
54
|
end
|
@@ -3,7 +3,7 @@ module Particle
|
|
3
3
|
# Configuration options for {Client}, defaulting to values in
|
4
4
|
# {Default}
|
5
5
|
module Configurable
|
6
|
-
# @!attribute [
|
6
|
+
# @!attribute [String] access_token
|
7
7
|
# @see http://docs.particle.io/core/api/#introduction-authentication
|
8
8
|
# @return [String] Particle access token for authentication
|
9
9
|
# @!attribute api_endpoint
|
data/lib/particle/connection.rb
CHANGED
@@ -57,15 +57,6 @@ module Particle
|
|
57
57
|
request :delete, url, options
|
58
58
|
end
|
59
59
|
|
60
|
-
# Make a HTTP HEAD request
|
61
|
-
#
|
62
|
-
# @param url [String] The path, relative to {#api_endpoint}
|
63
|
-
# @param options [Hash] Query and header params for request
|
64
|
-
# @return [Sawyer::Resource]
|
65
|
-
def head(url, options = {})
|
66
|
-
request :head, url, parse_query_and_convenience_headers(options)
|
67
|
-
end
|
68
|
-
|
69
60
|
# Hypermedia agent for the Particle API
|
70
61
|
#
|
71
62
|
# @return [Sawyer::Agent]
|
@@ -98,13 +89,21 @@ module Particle
|
|
98
89
|
|
99
90
|
def request(method, path, data, options = {})
|
100
91
|
if data.is_a?(Hash)
|
101
|
-
options[:query]
|
102
|
-
options[:headers]
|
92
|
+
options[:query] ||= data.delete(:query) || {}
|
93
|
+
options[:headers] ||= data.delete(:headers) || {}
|
103
94
|
if accept = data.delete(:accept)
|
104
95
|
options[:headers][:accept] = accept
|
105
96
|
end
|
106
97
|
end
|
107
98
|
|
99
|
+
username = options.delete(:username)
|
100
|
+
password = options.delete(:password)
|
101
|
+
if username && password
|
102
|
+
options[:headers] ||= {}
|
103
|
+
options[:headers][:authorization] =
|
104
|
+
basic_auth_header(username, password)
|
105
|
+
end
|
106
|
+
|
108
107
|
@last_response = response = agent.call(method, URI::Parser.new.escape(path.to_s), data, options)
|
109
108
|
response.data
|
110
109
|
end
|
@@ -120,6 +119,12 @@ module Particle
|
|
120
119
|
|
121
120
|
opts
|
122
121
|
end
|
122
|
+
|
123
|
+
# Temporarily set the Authorization to use basic auth
|
124
|
+
def basic_auth_header(username, password)
|
125
|
+
Faraday::Request.lookup_middleware(:basic_auth).
|
126
|
+
header(username, password)
|
127
|
+
end
|
123
128
|
end
|
124
129
|
end
|
125
130
|
|
data/lib/particle/device.rb
CHANGED
@@ -1,20 +1,20 @@
|
|
1
|
+
require 'particle/model'
|
2
|
+
|
1
3
|
module Particle
|
2
4
|
|
3
5
|
# Domain model for one Particle device
|
4
|
-
class Device
|
6
|
+
class Device < Model
|
5
7
|
ID_REGEX = /\h{24}/
|
6
8
|
|
7
9
|
def initialize(client, attributes)
|
8
|
-
|
9
|
-
|
10
|
+
super(client, attributes)
|
11
|
+
|
10
12
|
if attributes.is_a? String
|
11
13
|
if attributes =~ ID_REGEX
|
12
|
-
{ id: attributes }
|
14
|
+
@attributes = { id: attributes }
|
13
15
|
else
|
14
|
-
{ name: attributes }
|
16
|
+
@attributes = { name: attributes }
|
15
17
|
end
|
16
|
-
else
|
17
|
-
attributes
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
@@ -32,17 +32,10 @@ module Particle
|
|
32
32
|
@attributes[:id] || @attributes[:name]
|
33
33
|
end
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
attributes[key.to_sym]
|
38
|
-
end
|
39
|
-
end
|
40
|
-
alias_method :connected?, :connected
|
35
|
+
attribute_reader :connected, :functions, :variables, :product_id,
|
36
|
+
:last_heard, :last_app, :last_ip_address
|
41
37
|
|
42
|
-
|
43
|
-
get_attributes unless @loaded
|
44
|
-
@attributes
|
45
|
-
end
|
38
|
+
alias_method :connected?, :connected
|
46
39
|
|
47
40
|
def get_attributes
|
48
41
|
@loaded = true
|
data/lib/particle/error.rb
CHANGED
@@ -0,0 +1,49 @@
|
|
1
|
+
module Particle
|
2
|
+
|
3
|
+
# Base class for domain models
|
4
|
+
class Model
|
5
|
+
def initialize(client, attributes)
|
6
|
+
@client = client
|
7
|
+
@attributes =
|
8
|
+
if attributes.is_a? String
|
9
|
+
{ id: attributes }
|
10
|
+
else
|
11
|
+
# Consider attributes loaded when passed in through constructor
|
12
|
+
@loaded = true
|
13
|
+
attributes
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Display the attributes when inspecting the object in the console
|
18
|
+
def inspect
|
19
|
+
"#<#{self.class} #{@attributes}>"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Accessor for the id
|
23
|
+
def id
|
24
|
+
@attributes[:id]
|
25
|
+
end
|
26
|
+
|
27
|
+
# Define accessor methods for attributes.
|
28
|
+
#
|
29
|
+
# Will load the attributes from the cloud if not already done
|
30
|
+
def self.attribute_reader(*keys)
|
31
|
+
keys.each do |key|
|
32
|
+
define_method key do
|
33
|
+
attributes[key]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Hash of all attributes returned by the cloud
|
39
|
+
def attributes
|
40
|
+
get_attributes unless @loaded
|
41
|
+
@attributes
|
42
|
+
end
|
43
|
+
|
44
|
+
# Load the model attributes with the correct API call in subclasses
|
45
|
+
def get_attributes
|
46
|
+
raise "Implement in subclasses and set @loaded = true"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Particle
|
2
|
+
|
3
|
+
# Domain model for one Particle token
|
4
|
+
class Token < Model
|
5
|
+
def initialize(client, attributes)
|
6
|
+
@client = client
|
7
|
+
@attributes =
|
8
|
+
if attributes.is_a? String
|
9
|
+
{ token: attributes }
|
10
|
+
else
|
11
|
+
# Consider attributes loaded when passed in through constructor
|
12
|
+
@loaded = true
|
13
|
+
attributes
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# The id of the token
|
18
|
+
def token
|
19
|
+
@attributes[:token]
|
20
|
+
end
|
21
|
+
alias_method :id, :token
|
22
|
+
alias_method :access_token, :token
|
23
|
+
|
24
|
+
attribute_reader :expires_at, :client
|
25
|
+
|
26
|
+
# Tokens can't be loaded. What you see is what you get...
|
27
|
+
def get_attributes
|
28
|
+
@loaded = true
|
29
|
+
@attributes
|
30
|
+
end
|
31
|
+
|
32
|
+
# Text representation of the token, masking the secret part
|
33
|
+
#
|
34
|
+
# @return [String]
|
35
|
+
def inspect
|
36
|
+
inspected = super
|
37
|
+
|
38
|
+
# Only show last 4 of token, secret
|
39
|
+
if id
|
40
|
+
inspected = inspected.gsub! id, "#{'*'*36}#{id[36..-1]}"
|
41
|
+
end
|
42
|
+
|
43
|
+
inspected
|
44
|
+
end
|
45
|
+
|
46
|
+
# Create a Particle token
|
47
|
+
# @param username [String] The username (email) used to log in to
|
48
|
+
# the Particle Cloud API
|
49
|
+
# @param password [String] The password used to log in to
|
50
|
+
# the Particle Cloud API
|
51
|
+
def create(username, password)
|
52
|
+
@client.create_token(username, password)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Remove a Particle token
|
56
|
+
# @param username [String] The username (email) used to log in to
|
57
|
+
# the Particle Cloud API
|
58
|
+
# @param password [String] The password used to log in to
|
59
|
+
# the Particle Cloud API
|
60
|
+
def remove(username, password)
|
61
|
+
@client.remove_token(username, password, self)
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.list_path
|
65
|
+
"v1/access_tokens"
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.create_path
|
69
|
+
"/oauth/token"
|
70
|
+
end
|
71
|
+
|
72
|
+
def path
|
73
|
+
"/v1/access_tokens/#{access_token}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/particle/version.rb
CHANGED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'particle/model'
|
2
|
+
|
3
|
+
module Particle
|
4
|
+
|
5
|
+
# Domain model for one Particle device
|
6
|
+
class Webhook < Model
|
7
|
+
def initialize(client, attributes)
|
8
|
+
super(client, attributes)
|
9
|
+
end
|
10
|
+
|
11
|
+
attribute_reader :url, :deviceID, :event, :created_at, :mydevices,
|
12
|
+
:requestType, :headers, :json, :query, :auth
|
13
|
+
|
14
|
+
# The response of the web server to a test message
|
15
|
+
# If nil, check error
|
16
|
+
def response
|
17
|
+
get_attributes unless @loaded
|
18
|
+
@response
|
19
|
+
end
|
20
|
+
|
21
|
+
# The error from the web server to a test message
|
22
|
+
# If nil, check response
|
23
|
+
def error
|
24
|
+
get_attributes unless @loaded
|
25
|
+
@error
|
26
|
+
end
|
27
|
+
|
28
|
+
# Force reloading the attributes for the webhook
|
29
|
+
def get_attributes
|
30
|
+
@loaded = true
|
31
|
+
result = @client.webhook_attributes(self)
|
32
|
+
@response = result[:response]
|
33
|
+
@error = result[:error]
|
34
|
+
@attributes = result[:webhook]
|
35
|
+
end
|
36
|
+
|
37
|
+
# Add a Particle webhook
|
38
|
+
def create
|
39
|
+
new_webhook = @client.create_webhook(@attributes)
|
40
|
+
@attributes = new_webhook.attributes
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
# Remove a Particle webhook
|
45
|
+
def remove
|
46
|
+
@client.remove_webhook(self)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.list_path
|
50
|
+
"v1/webhooks"
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.create_path
|
54
|
+
"v1/webhooks"
|
55
|
+
end
|
56
|
+
|
57
|
+
def path
|
58
|
+
"/v1/webhooks/#{id}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
data/lib/particle.rb
CHANGED
@@ -11,6 +11,25 @@ module Particle
|
|
11
11
|
@client = Particle::Client.new(options)
|
12
12
|
end
|
13
13
|
|
14
|
+
# Authenticate with Particle and start using the new token as the
|
15
|
+
# global token
|
16
|
+
#
|
17
|
+
# Delegates actual functionality to the client.
|
18
|
+
#
|
19
|
+
# @param username [String] The username (email) used to log in to
|
20
|
+
# the Particle Cloud API
|
21
|
+
# @param password [String] The password used to log in to
|
22
|
+
# the Particle Cloud API
|
23
|
+
# @param options [Hash] Additional Particle Cloud API options to
|
24
|
+
# create the token.
|
25
|
+
# @return [Token] The token object
|
26
|
+
# @see {Particle::Client::Tokens#login}
|
27
|
+
def login(username, password, options = {})
|
28
|
+
token = client.login(username, password, options)
|
29
|
+
self.access_token = token.access_token
|
30
|
+
token
|
31
|
+
end
|
32
|
+
|
14
33
|
private
|
15
34
|
|
16
35
|
def respond_to_missing?(method_name, include_private = false)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: particlerb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julien Vanier
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-06-
|
11
|
+
date: 2015-06-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sawyer
|
@@ -149,13 +149,20 @@ files:
|
|
149
149
|
- lib/particle.rb
|
150
150
|
- lib/particle/client.rb
|
151
151
|
- lib/particle/client/devices.rb
|
152
|
+
- lib/particle/client/publish.rb
|
153
|
+
- lib/particle/client/tokens.rb
|
154
|
+
- lib/particle/client/webhooks.rb
|
152
155
|
- lib/particle/configurable.rb
|
153
156
|
- lib/particle/connection.rb
|
154
157
|
- lib/particle/default.rb
|
155
158
|
- lib/particle/device.rb
|
156
159
|
- lib/particle/error.rb
|
160
|
+
- lib/particle/event.rb
|
161
|
+
- lib/particle/model.rb
|
157
162
|
- lib/particle/response/raise_error.rb
|
163
|
+
- lib/particle/token.rb
|
158
164
|
- lib/particle/version.rb
|
165
|
+
- lib/particle/webhook.rb
|
159
166
|
- lib/particlerb.rb
|
160
167
|
- particlerb.gemspec
|
161
168
|
homepage: https://github.com/monkbroc/particlerb
|