beebotte 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/Gemfile +4 -0
- data/Guardfile +11 -0
- data/README.md +61 -0
- data/Rakefile +10 -0
- data/beebotte.gemspec +36 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/beebotte.rb +345 -0
- data/lib/beebotte/version.rb +3 -0
- metadata +235 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6a342811e6b3041aacf419ffa54d37bc9ff1904e
|
4
|
+
data.tar.gz: ca0f73eda5f8a58aa780dcb913ef52769b413e4c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bbea1488fbed398f5640f684ef9942a5a84f40bbac798685cd53de98ae24e62cebae1f24b45d4f03e53fef040eac57e393a824fa4f89500506bbefd308c5bcf2
|
7
|
+
data.tar.gz: fa588fa2d9acac1a8a5acfc220c69bb741afb5c726134a2f823363998933c5a08b59851e765072f1ab2e035de60e51bdadd6b37730ce80ed69d0a692aef1145d
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
Beebotte ruby gem
|
2
|
+
=================
|
3
|
+
|
4
|
+
|
5
|
+
THIS IS ALPHA SOFTWARE - USE AT YOUR OWN PERIL
|
6
|
+
|
7
|
+
Example usage:
|
8
|
+
--------------
|
9
|
+
```
|
10
|
+
b = Beebotte::Connector.new("<yourApiKey>", "<yourSecretKey>", 'api.beebotte.com', 443)
|
11
|
+
|
12
|
+
channel_name = SecureRandom.hex(8)
|
13
|
+
resource_name = SecureRandom.hex(8)
|
14
|
+
resource_name2 = SecureRandom.hex(8)
|
15
|
+
|
16
|
+
puts "\n\n---------\nadd_channels:"
|
17
|
+
channel = {"name":channel_name, "resources": [{"name":resource_name, "vtype":"any"}]}
|
18
|
+
b.add_channel(channel) {|r, code| puts "(#{code}) #{r.inspect}" }
|
19
|
+
|
20
|
+
puts "\n\n---------\nwrite:"
|
21
|
+
b.write(channel_name, resource_name, { id: rand(1000000), status:"A sample write message"})
|
22
|
+
|
23
|
+
puts "\n\n---------\npublish:"
|
24
|
+
b.publish(channel_name, resource_name, { id: rand(1000000), status:"A sample publish message"})
|
25
|
+
|
26
|
+
puts "\n\n---------\nread:"
|
27
|
+
b.read({channel: channel_name, resource: resource_name, limit: 1}) {|r, code| puts "(#{code}) #{r.inspect}"}
|
28
|
+
|
29
|
+
|
30
|
+
puts "\n\n---------\nadd_resource:"
|
31
|
+
resource2 = {"name":resource_name2, "vtype":"any"}
|
32
|
+
b.add_resource(channel_name, resource2) {|r, code| puts "(#{code}) #{r.inspect}"}
|
33
|
+
|
34
|
+
puts "\n\n---------\ndel_resource:"
|
35
|
+
b.del_resource(channel_name, resource_name2) {|r, code| puts "(#{code}) #{r.inspect}"}
|
36
|
+
|
37
|
+
puts "\n\n---------\nget_channels:"
|
38
|
+
b.get_channels {|r, code| puts "(#{code}) #{r.inspect}"}
|
39
|
+
|
40
|
+
puts "\n\n---------\nget_channel:"
|
41
|
+
b.get_channel(channel_name) {|r, code| puts "(#{code}) #{r.inspect}"}
|
42
|
+
|
43
|
+
puts "\n\n---------\nget_connections:"
|
44
|
+
b.get_connections {|r, code| puts "(#{code}) #{r.inspect}"}
|
45
|
+
|
46
|
+
puts "\n\n---------\nget_resources:"
|
47
|
+
b.get_resources(resource_name) {|r, code| puts "(#{code}) #{r.inspect}"}
|
48
|
+
|
49
|
+
|
50
|
+
puts "\n\n---------\ndel_channel:"
|
51
|
+
b.del_channel(channel_name) {|r, code| puts "(#{code}) #{r.inspect}"}
|
52
|
+
|
53
|
+
```
|
54
|
+
|
55
|
+
TODO:
|
56
|
+
-----
|
57
|
+
1. Documentation
|
58
|
+
1. Testing
|
59
|
+
1. Token authentication for REST API
|
60
|
+
1. Bulk API
|
61
|
+
1. Stream API
|
data/Rakefile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'rspec/core/rake_task'
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
|
4
|
+
# Default directory to look in is `/specs`
|
5
|
+
# Run with `rake spec`
|
6
|
+
RSpec::Core::RakeTask.new(:spec) do |task|
|
7
|
+
task.rspec_opts = ['--color', '--format', 'nested']
|
8
|
+
end
|
9
|
+
|
10
|
+
task :default => :spec
|
data/beebotte.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'beebotte/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "beebotte"
|
8
|
+
spec.version = Beebotte::VERSION
|
9
|
+
spec.authors = ["Mike Kazmier"]
|
10
|
+
spec.email = ["dakazmier@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = "Beebotte REST API connector"
|
13
|
+
spec.description = "A pure ruby implementation of the BBT connector for beebotte's REST api"
|
14
|
+
spec.homepage = "https://github.com/DaKaZ/bbt_ruby"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
17
|
+
f.match(%r{^(test|spec|features)/})
|
18
|
+
end
|
19
|
+
spec.bindir = "exe"
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_runtime_dependency 'openssl'
|
24
|
+
spec.add_runtime_dependency 'json'
|
25
|
+
spec.add_runtime_dependency 'rest-client', '>= 2.0.0'
|
26
|
+
spec.add_runtime_dependency 'classy_hash'
|
27
|
+
spec.add_runtime_dependency 'mqtt'
|
28
|
+
spec.add_development_dependency "rspec"
|
29
|
+
spec.add_development_dependency "rspec-nc"
|
30
|
+
spec.add_development_dependency "guard"
|
31
|
+
spec.add_development_dependency "guard-rspec"
|
32
|
+
spec.add_development_dependency "pry"
|
33
|
+
spec.add_development_dependency "webmock"
|
34
|
+
spec.add_development_dependency "bundler", "~> 1.14"
|
35
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
36
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "beebotte"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/lib/beebotte.rb
ADDED
@@ -0,0 +1,345 @@
|
|
1
|
+
require "beebotte/version"
|
2
|
+
|
3
|
+
module Beebotte
|
4
|
+
require 'openssl'
|
5
|
+
require 'json'
|
6
|
+
require 'rest-client'
|
7
|
+
require 'base64'
|
8
|
+
require 'classy_hash'
|
9
|
+
require 'mqtt'
|
10
|
+
|
11
|
+
class Connector
|
12
|
+
|
13
|
+
ATTRIBUTE_TYPE_LABELS = [
|
14
|
+
'any',
|
15
|
+
'number',
|
16
|
+
'string',
|
17
|
+
'boolean',
|
18
|
+
'object',
|
19
|
+
'function',
|
20
|
+
'array',
|
21
|
+
'alphabetic',
|
22
|
+
'alphanumeric',
|
23
|
+
'decimal',
|
24
|
+
'rate',
|
25
|
+
'percentage',
|
26
|
+
'email',
|
27
|
+
'gps',
|
28
|
+
'cpu',
|
29
|
+
'memory',
|
30
|
+
'netif',
|
31
|
+
'disk',
|
32
|
+
'temperature',
|
33
|
+
'humidity',
|
34
|
+
'body_temp',
|
35
|
+
]
|
36
|
+
|
37
|
+
def initialize(apiKey, secretKey, hostname='api.beebotte.com', port=80, protocol=nil, headers=nil)
|
38
|
+
@apiKey = apiKey
|
39
|
+
@secretKey = secretKey
|
40
|
+
@hostname = hostname
|
41
|
+
@port = port
|
42
|
+
@protocol = protocol.is_a?(String) ? protocol.downcase : ((@port == 443) ? 'https' : 'http')
|
43
|
+
@headers = headers || {
|
44
|
+
"Content-type" => 'application/json',
|
45
|
+
"Content-MD5" => '',
|
46
|
+
"User-Agent" => get_useragent_string
|
47
|
+
}
|
48
|
+
|
49
|
+
@resource_schema = {
|
50
|
+
name: CH::G.string_length(2..30),
|
51
|
+
label: [:optional, CH::G.string_length(0..30) ],
|
52
|
+
description: [:optional, String ],
|
53
|
+
vtype: Set.new(ATTRIBUTE_TYPE_LABELS),
|
54
|
+
sendOnSubscribe: [:optional, TrueClass]
|
55
|
+
}
|
56
|
+
|
57
|
+
@channel_schema = {
|
58
|
+
name: CH::G.string_length(2..30),
|
59
|
+
label: [:optional, CH::G.string_length(0..30) ],
|
60
|
+
description: [:optional, String ],
|
61
|
+
resources: [ [ @resource_schema ] ],
|
62
|
+
ispublic: [:optional, TrueClass]
|
63
|
+
}
|
64
|
+
|
65
|
+
@read_params_schema = {
|
66
|
+
channel: CH::G.string_length(2..30),
|
67
|
+
resource: CH::G.string_length(2..30),
|
68
|
+
limit: [:optional, 1..2000 ],
|
69
|
+
'time-range': [:optional, String],
|
70
|
+
'start-time': [:optional, String],
|
71
|
+
'end-time': [:optional, String],
|
72
|
+
filter: [:optional, String],
|
73
|
+
'sample-rate': [:optional, 1..10000]
|
74
|
+
}
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
# write persistent information
|
79
|
+
def write(channel, resource, data, &block)
|
80
|
+
raise ArgumentError, 'Channel name must be a string' unless channel.is_a?(String)
|
81
|
+
raise ArgumentError, 'Resource name must be a string' unless resource.is_a?(String)
|
82
|
+
raise ArgumentError, 'Data must be a hash object' unless data.is_a?(Hash)
|
83
|
+
|
84
|
+
body = {data: data}
|
85
|
+
response = post_data("/v1/data/write/#{channel}/#{resource}", body.to_json)
|
86
|
+
block.call(response.body, response.code) if block
|
87
|
+
end
|
88
|
+
|
89
|
+
# publish transient information
|
90
|
+
def publish(channel, resource, data, &block)
|
91
|
+
raise ArgumentError, 'Channel name must be a string' unless channel.is_a?(String)
|
92
|
+
raise ArgumentError, 'Resource name must be a string' unless resource.is_a?(String)
|
93
|
+
raise ArgumentError, 'Data must be a hash object' unless data.is_a?(Hash)
|
94
|
+
body = {data: data}
|
95
|
+
response = post_data("/v1/data/publish/#{channel}/#{resource}", body.to_json)
|
96
|
+
block.call(response.body, response.code) if block
|
97
|
+
end
|
98
|
+
|
99
|
+
# Read persisted messages from the specified channel and resource
|
100
|
+
#
|
101
|
+
# ==== Attributes
|
102
|
+
#
|
103
|
+
# * +channel+ - String: the channel name
|
104
|
+
# * +resource+ - String: the resource name
|
105
|
+
# * +params+ - Hash: the query parameters: 'time-range', 'start-time', 'end-time', 'limit', 'filter', 'sample-rate'
|
106
|
+
#
|
107
|
+
def read(params, &block)
|
108
|
+
ClassyHash.validate(params, @read_params_schema, strict: true)
|
109
|
+
params[:limit] ||= 750
|
110
|
+
rtn = {}
|
111
|
+
uri = "/v1/data/read/#{params[:channel]}/#{params[:resource]}"
|
112
|
+
[:channel, :resource].each {|k| params.delete(k) }
|
113
|
+
puts "PARAMS: #{params.inspect}"
|
114
|
+
response = get_data(uri, params)
|
115
|
+
rtn = JSON.parse(response.body) if response.code >= 200 && response.code < 300
|
116
|
+
block.call(rtn, response.code) if block
|
117
|
+
end
|
118
|
+
|
119
|
+
def get_connections(user=nil, &block)
|
120
|
+
resource = user.nil? ? [] : {}
|
121
|
+
uri = "/v1/connections" + (resource.is_a?(String) ? "/#{resource}" : '')
|
122
|
+
response = get_data(uri)
|
123
|
+
resource = JSON.parse(response.body) if response.code >= 200 && response.code < 300
|
124
|
+
block.call(resource, response.code) if block
|
125
|
+
end
|
126
|
+
|
127
|
+
def get_conection(user, &block)
|
128
|
+
raise ArgumentError, 'User name must be a string' unless user.is_a?(String)
|
129
|
+
get_connections(user, &block)
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
# get_channels { |response| puts response.body }
|
134
|
+
def get_channels(channel=nil, &block)
|
135
|
+
rtn = {}
|
136
|
+
uri = "/v1/channels" + (channel.is_a?(String) ? "/#{channel}" : '')
|
137
|
+
response = get_data(uri)
|
138
|
+
rtn = JSON.parse(response.body) if response.code >= 200 && response.code < 300
|
139
|
+
block.call(rtn, response.code) if block
|
140
|
+
end
|
141
|
+
|
142
|
+
def get_channel(channel, &block)
|
143
|
+
raise ArgumentError, 'Channel name must be a string' unless channel.is_a?(String)
|
144
|
+
get_channels(channel, &block)
|
145
|
+
end
|
146
|
+
|
147
|
+
def add_channel(channel, &block)
|
148
|
+
ClassyHash.validate(channel, @channel_schema, strict: true)
|
149
|
+
# validate that no resource descriptions are the same as the channel name
|
150
|
+
raise ArgumentError, 'Must have at least one resource' if channel[:resources].length < 1
|
151
|
+
channel[:resources].each do |r|
|
152
|
+
raise ArgumentError, 'Resource :name must not equal Channel :name' if r[:name] == channel[:name]
|
153
|
+
end
|
154
|
+
response = post_data("/v1/channels", channel.to_json)
|
155
|
+
if response.code >= 200 && response.code < 300
|
156
|
+
get_channel(channel[:name], &block)
|
157
|
+
else
|
158
|
+
block.call({}, response.code) if block
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def del_channel(channel, &block)
|
163
|
+
raise ArgumentError, 'Channel name must be a string' unless channel.is_a?(String)
|
164
|
+
response = del_data("/v1/channels/#{channel}")
|
165
|
+
block.call(response.body, response.code) if block
|
166
|
+
end
|
167
|
+
|
168
|
+
def get_resources(channel, resource='*', &block)
|
169
|
+
raise ArgumentError, 'Channel name must be a string' unless channel.is_a?(String)
|
170
|
+
rtn = {}
|
171
|
+
params = {
|
172
|
+
resource: resource
|
173
|
+
}
|
174
|
+
response = get_data("/v1/channels/#{channel}/resources", params)
|
175
|
+
rtn = JSON.parse(response.body) if response.code >= 200 && response.code < 300
|
176
|
+
block.call(rtn, response.code) if block
|
177
|
+
end
|
178
|
+
|
179
|
+
def get_resource(channel, resource, &block)
|
180
|
+
raise ArgumentError, 'Channel name must be a string' unless channel.is_a?(String)
|
181
|
+
raise ArgumentError, 'Resource name must be a string' unless resource.is_a?(String)
|
182
|
+
get_resources(channel, resource, &block)
|
183
|
+
end
|
184
|
+
|
185
|
+
#
|
186
|
+
# {resource: {name, description, type, vtype, ispublic}}
|
187
|
+
#
|
188
|
+
def add_resource(channel, resource, &block)
|
189
|
+
raise ArgumentError, 'Channel name must be a string' unless channel.is_a?(String)
|
190
|
+
ClassyHash.validate(resource, @resource_schema, strict: true)
|
191
|
+
# validate that no resource descriptions are the same as the channel name
|
192
|
+
raise ArgumentError, 'Resource :name must not equal Channel :name' if resource[:name] == channel
|
193
|
+
response = post_data("/v1/channels/#{channel}/resources", resource.to_json)
|
194
|
+
if response.code >= 200 && response.code < 300
|
195
|
+
get_resource(channel, resource[:name], &block)
|
196
|
+
else
|
197
|
+
block.call({}, response.code) if block
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def del_resource(channel, resource, &block)
|
202
|
+
raise ArgumentError, 'Channel name must be a string' unless channel.is_a?(String)
|
203
|
+
raise ArgumentError, 'Resource name must be a string' unless resource.is_a?(String)
|
204
|
+
response = del_data("/v1/channels/#{channel}/resources/#{resource}")
|
205
|
+
block.call(response.body, response.code) if block
|
206
|
+
end
|
207
|
+
|
208
|
+
private
|
209
|
+
|
210
|
+
def get_data(uri, params=nil)
|
211
|
+
@headers["Content-MD5"] = ''
|
212
|
+
if params && params.is_a?(Hash)
|
213
|
+
params.each_with_index do |a, index|
|
214
|
+
uri << ((index == 0) ? "?" : "&")
|
215
|
+
uri << "#{a[0]}=#{a[1]}"
|
216
|
+
end
|
217
|
+
end
|
218
|
+
signature = get_signature('GET', uri, @headers, @secretKey)
|
219
|
+
@headers["Authorization"] = "#{@apiKey}:#{signature}"
|
220
|
+
url = "#{@protocol}://#{@hostname}:#{@port}#{uri}"
|
221
|
+
puts "URL: #{url}"
|
222
|
+
response = RestClient.get(url, @headers)
|
223
|
+
end
|
224
|
+
|
225
|
+
def post_data(uri, body=nil)
|
226
|
+
@headers["Content-MD5"] = body.nil? ? '' : Digest::MD5.base64digest(body)
|
227
|
+
signature = get_signature('POST', uri, @headers, @secretKey)
|
228
|
+
@headers["Authorization"] = "#{@apiKey}:#{signature}"
|
229
|
+
url = "#{@protocol}://#{@hostname}:#{@port}#{uri}"
|
230
|
+
puts "URL: #{url}"
|
231
|
+
puts "BODY: #{body}"
|
232
|
+
response = RestClient.post(url, body, @headers)
|
233
|
+
end
|
234
|
+
|
235
|
+
def del_data(uri)
|
236
|
+
@headers["Content-MD5"] = ''
|
237
|
+
signature = get_signature('DELETE', uri, @headers, @secretKey)
|
238
|
+
@headers["Authorization"] = "#{@apiKey}:#{signature}"
|
239
|
+
url = "#{@protocol}://#{@hostname}:#{@port}#{uri}"
|
240
|
+
puts "URL: #{url}"
|
241
|
+
response = RestClient.delete(url, @headers)
|
242
|
+
end
|
243
|
+
|
244
|
+
def get_signature(method, path, headers, secretKey)
|
245
|
+
@headers["Date"] = Time.now.utc.httpdate
|
246
|
+
raise ArgumentError, 'Beebotte Secret key is missing' if secretKey.nil? || !secretKey.is_a?(String)
|
247
|
+
raise ArgumentError, 'Invalid method' unless (method == 'GET' || method == 'PUT' || method == 'POST' || method == 'DELETE')
|
248
|
+
raise ArgumentError, 'Invalid path' unless path.is_a?(String) && path.match(/^\//)
|
249
|
+
stringToSign = "#{method}\n#{headers['Content-MD5']}\n#{headers["Content-type"]}\n#{headers["Date"]}\n#{path}"
|
250
|
+
puts "stiringToSign= \"#{stringToSign}\""
|
251
|
+
signature = sha1_sign(secretKey, stringToSign)
|
252
|
+
end
|
253
|
+
|
254
|
+
def sha1_sign(secretKey, stringToSign)
|
255
|
+
digest = OpenSSL::Digest.new('sha1')
|
256
|
+
hmac = OpenSSL::HMAC.digest(digest, secretKey, stringToSign)
|
257
|
+
signature = Base64.strict_encode64(hmac)
|
258
|
+
end
|
259
|
+
|
260
|
+
def get_useragent_string
|
261
|
+
return "beebotte ruby SDK v#{Beebotte::VERSION}"
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
class Stream
|
266
|
+
attr_accessor :token, :host, :port, :ssl
|
267
|
+
attr_reader :subsciptions
|
268
|
+
|
269
|
+
class NotConnected < StandardError; end
|
270
|
+
class NoSubscriptions < StandardError; end
|
271
|
+
|
272
|
+
def initialize(opts = {})
|
273
|
+
@token = opts[:token]
|
274
|
+
@apiKey = opts[:api_key]
|
275
|
+
@secretKey = opts[:secret_key]
|
276
|
+
raise ArguementError, 'Must set token OR api_key and secret key' if (@token.nil? && (@apiKey.nil? || @secretKey.nil?))
|
277
|
+
@host = opts[:host] || "mqtt.beebotte.com"
|
278
|
+
@port = opts[:port] || 1883
|
279
|
+
@ssl = opts[:ssl] || false
|
280
|
+
@subscrptions = []
|
281
|
+
@client = MQTT::Client.new
|
282
|
+
end
|
283
|
+
|
284
|
+
def connect
|
285
|
+
@client.disconnect() if @client.connected?
|
286
|
+
@client.host = @host
|
287
|
+
@client.port = @port
|
288
|
+
@client.ssl = @ssl
|
289
|
+
@client.username = @client.password = nil
|
290
|
+
@subscriptions = []
|
291
|
+
if @token
|
292
|
+
@client.username = "token:#{@token}"
|
293
|
+
end
|
294
|
+
@client.connect()
|
295
|
+
@client.connected?
|
296
|
+
end
|
297
|
+
|
298
|
+
def disconnect
|
299
|
+
@subscriptions = []
|
300
|
+
@client.disconnect()
|
301
|
+
end
|
302
|
+
|
303
|
+
def connected?
|
304
|
+
@client.connected?
|
305
|
+
end
|
306
|
+
|
307
|
+
def get(&block)
|
308
|
+
raise NoSubscriptions if @subscriptions.length == 0
|
309
|
+
@client.get(&block)
|
310
|
+
end
|
311
|
+
|
312
|
+
def publish(channel, resource, data)
|
313
|
+
raise ArgumentError, 'Channel name must be a string' unless channel.is_a?(String) && channel.length.between?(2,30)
|
314
|
+
raise ArgumentError, 'Resource name must be a string' unless resource.is_a?(String) && resource.length.between?(2,30)
|
315
|
+
raise ArgumentError, 'Data name must be a Hash' unless data.is_a?(Hash)
|
316
|
+
data = {data: data, write: false }
|
317
|
+
@client.publish("#{channel}/#{resource}" , data.to_json)
|
318
|
+
end
|
319
|
+
|
320
|
+
|
321
|
+
def write(channel, resource, data)
|
322
|
+
raise ArgumentError, 'Channel name must be a string' unless channel.is_a?(String) && channel.length.between?(2,30)
|
323
|
+
raise ArgumentError, 'Resource name must be a string' unless resource.is_a?(String) && resource.length.between?(2,30)
|
324
|
+
raise ArgumentError, 'Data name must be a Hash' unless data.is_a?(Hash)
|
325
|
+
data = {data: data, write: true }
|
326
|
+
@client.publish("#{channel}/#{resource}" , data.to_json)
|
327
|
+
|
328
|
+
end
|
329
|
+
|
330
|
+
def subscribe(topic)
|
331
|
+
raise ArgumentError, "Topic must be in the form of 'channel/resource'" unless topic.is_a?(String) && topic.match(/^[a-zA-Z0-9_]*\/[a-zA-Z0-9_]*$/)
|
332
|
+
raise NotConnected unless connected?
|
333
|
+
return true if @subscriptions.include?(topic)
|
334
|
+
if @client.subscribe(topic)
|
335
|
+
@subscriptions << topic
|
336
|
+
return true
|
337
|
+
else
|
338
|
+
return false
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
metadata
ADDED
@@ -0,0 +1,235 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: beebotte
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mike Kazmier
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-04-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: openssl
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rest-client
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.0.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.0.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: classy_hash
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mqtt
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec-nc
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: guard
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: guard-rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: pry
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: webmock
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: bundler
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - "~>"
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '1.14'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - "~>"
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '1.14'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: rake
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - "~>"
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '10.0'
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - "~>"
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '10.0'
|
195
|
+
description: A pure ruby implementation of the BBT connector for beebotte's REST api
|
196
|
+
email:
|
197
|
+
- dakazmier@gmail.com
|
198
|
+
executables: []
|
199
|
+
extensions: []
|
200
|
+
extra_rdoc_files: []
|
201
|
+
files:
|
202
|
+
- ".gitignore"
|
203
|
+
- Gemfile
|
204
|
+
- Guardfile
|
205
|
+
- README.md
|
206
|
+
- Rakefile
|
207
|
+
- beebotte.gemspec
|
208
|
+
- bin/console
|
209
|
+
- bin/setup
|
210
|
+
- lib/beebotte.rb
|
211
|
+
- lib/beebotte/version.rb
|
212
|
+
homepage: https://github.com/DaKaZ/bbt_ruby
|
213
|
+
licenses: []
|
214
|
+
metadata: {}
|
215
|
+
post_install_message:
|
216
|
+
rdoc_options: []
|
217
|
+
require_paths:
|
218
|
+
- lib
|
219
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
220
|
+
requirements:
|
221
|
+
- - ">="
|
222
|
+
- !ruby/object:Gem::Version
|
223
|
+
version: '0'
|
224
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
225
|
+
requirements:
|
226
|
+
- - ">="
|
227
|
+
- !ruby/object:Gem::Version
|
228
|
+
version: '0'
|
229
|
+
requirements: []
|
230
|
+
rubyforge_project:
|
231
|
+
rubygems_version: 2.6.11
|
232
|
+
signing_key:
|
233
|
+
specification_version: 4
|
234
|
+
summary: Beebotte REST API connector
|
235
|
+
test_files: []
|