thingdom 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.
- data/.gitignore +34 -0
- data/LICENSE +22 -0
- data/README.md +45 -0
- data/lib/cacert.pem +3894 -0
- data/lib/httpHelper.rb +94 -0
- data/lib/tasks/baseTask.rb +57 -0
- data/lib/tasks/feedTask.rb +18 -0
- data/lib/tasks/statusTask.rb +49 -0
- data/lib/tasks/thingTask.rb +15 -0
- data/lib/thing.rb +68 -0
- data/lib/thingdom.rb +34 -0
- data/lib/thingdom/version.rb +3 -0
- data/lib/webService.rb +101 -0
- data/thingdom.gemspec +20 -0
- metadata +60 -0
data/lib/httpHelper.rb
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#
|
|
2
|
+
# A class that handles all HTTP communication with Thingdom.
|
|
3
|
+
#
|
|
4
|
+
require 'rubygems'
|
|
5
|
+
require 'net/https'
|
|
6
|
+
require 'uri'
|
|
7
|
+
require 'json'
|
|
8
|
+
|
|
9
|
+
class HttpHelper
|
|
10
|
+
|
|
11
|
+
#
|
|
12
|
+
# Constructor method for HttpHelper. Initialize local data.
|
|
13
|
+
#
|
|
14
|
+
def initialize()
|
|
15
|
+
@uri = URI.parse 'https://api.thingdom.io'
|
|
16
|
+
@path = '/1.1'
|
|
17
|
+
@request_counter = 0;
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
#
|
|
21
|
+
# Perform a HTTP GET request.
|
|
22
|
+
#
|
|
23
|
+
# @param [String] requestPath Contains path and optional query parameters (e.g. path/to/somewhere?param1=1¶m2=2)
|
|
24
|
+
# @return [Hash] The request response.
|
|
25
|
+
#
|
|
26
|
+
def get_data( requestPath )
|
|
27
|
+
do_request( requestPath )
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
#
|
|
31
|
+
# Perform a HTTP Post request.
|
|
32
|
+
#
|
|
33
|
+
# @param [String] requestPath Contains path to where data will be posted (e.g. path/to/post/endpoint)
|
|
34
|
+
# @param [Hash] data The data to be posted.
|
|
35
|
+
# @return [Hash] The request response.
|
|
36
|
+
#
|
|
37
|
+
def post_data( requestPath, data )
|
|
38
|
+
@request_counter += 1
|
|
39
|
+
data[:counter] = @request_counter
|
|
40
|
+
data[:time] = Time.now.strftime( '%Y/%m/%d %H:%M:%S' )
|
|
41
|
+
do_request( requestPath, data )
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# ***************************************************************************
|
|
45
|
+
# Helper Methods
|
|
46
|
+
# ***************************************************************************
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
#
|
|
50
|
+
# Convert JSON string to a hash where all keys are Symbols.
|
|
51
|
+
#
|
|
52
|
+
# @param [String] jsonString The JSON string.
|
|
53
|
+
# @return [Hash] A hash table representation of the JSON string.
|
|
54
|
+
#
|
|
55
|
+
def json_to_hash( jsonString )
|
|
56
|
+
jsonHash = JSON.parse( jsonString )
|
|
57
|
+
jsonHash = Hash[jsonHash.map{ |k, v| [k.to_sym, v] }]
|
|
58
|
+
return jsonHash
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
#
|
|
62
|
+
# Perform HTTP request.
|
|
63
|
+
#
|
|
64
|
+
# @param [String] requestPath Contains path to where data will be retrieved or posted (e.g. path/to/post-or-get/endpoint)
|
|
65
|
+
# @param [Hash] data The data to be posted.
|
|
66
|
+
# @return [Hash] The request response.
|
|
67
|
+
#
|
|
68
|
+
def do_request( requestPath, data = nil )
|
|
69
|
+
#
|
|
70
|
+
# Initialize HTTPS.
|
|
71
|
+
#
|
|
72
|
+
https = Net::HTTP.new( @uri.host, @uri.port )
|
|
73
|
+
https.use_ssl = true
|
|
74
|
+
https.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
75
|
+
https.ca_file = File.join( File.dirname( __FILE__ ), 'cacert.pem' )
|
|
76
|
+
#
|
|
77
|
+
# Build request.
|
|
78
|
+
#
|
|
79
|
+
if( data.nil? )
|
|
80
|
+
request = Net::HTTP::Get.new( @path + '/' + requestPath )
|
|
81
|
+
else
|
|
82
|
+
request = Net::HTTP::Post.new( @path + '/' + requestPath )
|
|
83
|
+
request.initialize_http_header( {'Content-Type' => 'application/json'} )
|
|
84
|
+
request.body = data.to_json
|
|
85
|
+
end
|
|
86
|
+
#
|
|
87
|
+
# Send request and convert JSON response into a hash.
|
|
88
|
+
#
|
|
89
|
+
response = https.request( request )
|
|
90
|
+
jsonHash = json_to_hash( response.body )
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
end
|
|
94
|
+
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
|
|
2
|
+
class BaseTask
|
|
3
|
+
|
|
4
|
+
def initialize( webService, thing, isThingTask )
|
|
5
|
+
@web = webService
|
|
6
|
+
@thing = thing
|
|
7
|
+
@isThingTask = isThingTask
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
#
|
|
11
|
+
# Perform the task.
|
|
12
|
+
#
|
|
13
|
+
def perform
|
|
14
|
+
response = {}
|
|
15
|
+
#
|
|
16
|
+
# Make sure thing is valid (id and code exists) before doing any work. The exception of course is if we
|
|
17
|
+
# are creating or retrieving a thing, in which case, id and code will not exist.
|
|
18
|
+
#
|
|
19
|
+
if( (@thing.id.to_s.length > 0 && @thing.code.length > 0) || @isThingTask )
|
|
20
|
+
#
|
|
21
|
+
# Do the task work. If token has expired, then reauthorize and try again.
|
|
22
|
+
#
|
|
23
|
+
begin
|
|
24
|
+
response = do_task_work()
|
|
25
|
+
if( response[:response] == 'token_expired')
|
|
26
|
+
@web.get_authorization()
|
|
27
|
+
response = do_task_work()
|
|
28
|
+
end
|
|
29
|
+
#
|
|
30
|
+
# On any exception, return an error response with the exception description in the message.
|
|
31
|
+
#
|
|
32
|
+
rescue
|
|
33
|
+
response[:response] = 'error'
|
|
34
|
+
response[:msg] = "#{$!.class} - #{$!.message}"
|
|
35
|
+
@thing.last_error = response[:msg]
|
|
36
|
+
end
|
|
37
|
+
#
|
|
38
|
+
# Thing has not been created. Return the appropriate error and message.
|
|
39
|
+
#
|
|
40
|
+
else
|
|
41
|
+
response[:response] = 'error'
|
|
42
|
+
response[:msg] = "Thing '#{@thing.name}' has not been created."
|
|
43
|
+
end
|
|
44
|
+
#
|
|
45
|
+
# Return the response.
|
|
46
|
+
#
|
|
47
|
+
return response
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
#
|
|
51
|
+
# This method should be overridden by the subclass and does the specific task work.
|
|
52
|
+
#
|
|
53
|
+
def do_task_work
|
|
54
|
+
return { :response => 'error', :msg => 'Task work method not implemented.' }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require_relative( 'baseTask' )
|
|
2
|
+
|
|
3
|
+
class FeedTask < BaseTask
|
|
4
|
+
|
|
5
|
+
def initialize( webService, thing, category, message, feedOptions )
|
|
6
|
+
super( webService, thing, false )
|
|
7
|
+
@web = webService
|
|
8
|
+
@thing = thing
|
|
9
|
+
@category = category
|
|
10
|
+
@message = message
|
|
11
|
+
@feedOptions = feedOptions
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def do_task_work
|
|
15
|
+
@web.add_feed( @thing, @category, @message, @feedOptions )
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require_relative( 'baseTask' )
|
|
2
|
+
|
|
3
|
+
class StatusTask < BaseTask
|
|
4
|
+
|
|
5
|
+
def initialize( webService, thing, *args )
|
|
6
|
+
super( webService, thing, false )
|
|
7
|
+
@web = webService
|
|
8
|
+
@thing = thing
|
|
9
|
+
@statusUpdates = statusArgsToArray( *args )
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def do_task_work
|
|
13
|
+
@web.add_status( @thing, @statusUpdates )
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# ***************************************************************************
|
|
17
|
+
# Helper methods.
|
|
18
|
+
# ***************************************************************************
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
#
|
|
22
|
+
# Convert status args into an array of hashes.
|
|
23
|
+
#
|
|
24
|
+
# @param [Array] *args The status args.
|
|
25
|
+
#
|
|
26
|
+
def statusArgsToArray( *args )
|
|
27
|
+
dataArray = Array.new
|
|
28
|
+
#
|
|
29
|
+
# If only one argument, then assume its already an array.
|
|
30
|
+
#
|
|
31
|
+
if( args.size == 1 )
|
|
32
|
+
dataArray = args[0]
|
|
33
|
+
#
|
|
34
|
+
# Otherwise, take the individual arguments, create a hash and insert into an array.
|
|
35
|
+
#
|
|
36
|
+
elsif( args.size >= 2 )
|
|
37
|
+
data = { :name => args[0], :value => args[1] }
|
|
38
|
+
if( args.size >= 3 )
|
|
39
|
+
data[:unit] = args[2]
|
|
40
|
+
end
|
|
41
|
+
dataArray.push( data )
|
|
42
|
+
end
|
|
43
|
+
#
|
|
44
|
+
# Return the result.
|
|
45
|
+
#
|
|
46
|
+
return dataArray
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
data/lib/thing.rb
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#
|
|
2
|
+
# A class that encapsulates a Thing
|
|
3
|
+
#
|
|
4
|
+
require_relative( './tasks/statusTask' )
|
|
5
|
+
require_relative( './tasks/feedTask' )
|
|
6
|
+
require_relative( './tasks/thingTask' )
|
|
7
|
+
|
|
8
|
+
class Thing
|
|
9
|
+
#
|
|
10
|
+
# Public Properties.
|
|
11
|
+
#
|
|
12
|
+
attr_accessor :id, :name, :product_type, :display_name, :code, :last_error
|
|
13
|
+
|
|
14
|
+
#
|
|
15
|
+
# Constructor method for Thing.
|
|
16
|
+
#
|
|
17
|
+
# @param [WebService] webService A web service used to communicate with Thingdom.
|
|
18
|
+
# @param [String] name The name of the Thing.
|
|
19
|
+
# @param [String] productType The product type the Thing belongs to.
|
|
20
|
+
# @param [String] displayName A user friendly name for Thing.
|
|
21
|
+
#
|
|
22
|
+
def initialize( webService, name, productType = '', displayName = '' )
|
|
23
|
+
@web = webService
|
|
24
|
+
@id = ''
|
|
25
|
+
@name = name
|
|
26
|
+
@product_type = productType
|
|
27
|
+
@display_name = displayName
|
|
28
|
+
@code = ''
|
|
29
|
+
#
|
|
30
|
+
# Perform task to get thing.
|
|
31
|
+
#
|
|
32
|
+
thingTask = ThingTask.new( @web, self )
|
|
33
|
+
response = thingTask.perform()
|
|
34
|
+
#
|
|
35
|
+
# If successful, then update the thing id and code.
|
|
36
|
+
#
|
|
37
|
+
if( response[:response] == 'success' )
|
|
38
|
+
@id = response[:thing_id]
|
|
39
|
+
@code = response[:code]
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
#
|
|
44
|
+
# Send a feed with additional feed options.
|
|
45
|
+
#
|
|
46
|
+
# @param [String] category A feed category that was defined during application registration.
|
|
47
|
+
# @param [String] message The feed message.
|
|
48
|
+
# @param [FeedOption] feedOptions Additional feed options: icon, progress bar, etc.
|
|
49
|
+
#
|
|
50
|
+
def feed( category, message, feedOptions = nil )
|
|
51
|
+
feedTask = FeedTask.new( @web, self, category, message, feedOptions )
|
|
52
|
+
feedTask.perform()
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
#
|
|
56
|
+
# Add or update a status item for this Thing.
|
|
57
|
+
#
|
|
58
|
+
# @param [String] name The status item name.
|
|
59
|
+
# @param [String] value The status item value.
|
|
60
|
+
# @param [String] unit The status item unit.
|
|
61
|
+
#
|
|
62
|
+
def status( *args )
|
|
63
|
+
statusTask = StatusTask.new( @web, self, *args )
|
|
64
|
+
statusTask.perform()
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
end
|
data/lib/thingdom.rb
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#
|
|
2
|
+
# The top level class that provides the Thingdom API.
|
|
3
|
+
#
|
|
4
|
+
require_relative 'webService'
|
|
5
|
+
require_relative 'thing'
|
|
6
|
+
|
|
7
|
+
class Thingdom
|
|
8
|
+
|
|
9
|
+
def initialize( appSecret )
|
|
10
|
+
#
|
|
11
|
+
# Create the web service.
|
|
12
|
+
#
|
|
13
|
+
@webService = WebService.new( appSecret )
|
|
14
|
+
@applicationToken = ''
|
|
15
|
+
#
|
|
16
|
+
# Authorize the application using the application secret. If successful, save the application token that
|
|
17
|
+
# is returned for all subsequent communications with Thingdom. Otherwise, raise an exception using the
|
|
18
|
+
# returned error message.
|
|
19
|
+
#
|
|
20
|
+
response = @webService.get_authorization()
|
|
21
|
+
result = response[:response] != 'error' && !response[:application_token].nil? && response[:application_token].length > 0
|
|
22
|
+
if( result )
|
|
23
|
+
@applicationToken = response[:application_token]
|
|
24
|
+
else
|
|
25
|
+
raise response[:msg]
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def get_thing( name, productType = '', displayName = '' )
|
|
30
|
+
thing = Thing.new( @webService, name, productType, displayName )
|
|
31
|
+
return thing
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
data/lib/webService.rb
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#
|
|
2
|
+
# A class that encapsulates the web services needed by the Ruby wrapper.
|
|
3
|
+
#
|
|
4
|
+
require_relative 'httpHelper'
|
|
5
|
+
|
|
6
|
+
class WebService
|
|
7
|
+
|
|
8
|
+
#
|
|
9
|
+
# Constructor method for WebService.
|
|
10
|
+
#
|
|
11
|
+
# @param [String] secret A secret identifier for an application obtained by the user during the registration process.
|
|
12
|
+
# Without this, the app will not have access to Thingdom.
|
|
13
|
+
#
|
|
14
|
+
def initialize( secret )
|
|
15
|
+
@httpHelper = HttpHelper.new()
|
|
16
|
+
@apiSecret = secret
|
|
17
|
+
@applicationToken = ''
|
|
18
|
+
@deviceSecret = 'none'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
#
|
|
22
|
+
# Ping Thingdom.
|
|
23
|
+
#
|
|
24
|
+
def ping_server()
|
|
25
|
+
@httpHelper.get_data( 'ping' )
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
#
|
|
29
|
+
# Authorize this application by sending the application secret to Thingdom. If valid, Thingdom will send back a token
|
|
30
|
+
# which must be used in subsequent communications.
|
|
31
|
+
#
|
|
32
|
+
# @return [Hash] A hash containing the authorization response:
|
|
33
|
+
# application_token - The token used in subsequent communications with Thingdom
|
|
34
|
+
# expires_in - The number of seconds remaining before above token expires.
|
|
35
|
+
# device_secret - A unique identifier for device running this application (always ruby in this case)
|
|
36
|
+
#
|
|
37
|
+
def get_authorization()
|
|
38
|
+
data = {
|
|
39
|
+
api_secret: @apiSecret,
|
|
40
|
+
device_secret: @deviceSecret
|
|
41
|
+
}
|
|
42
|
+
response = @httpHelper.post_data( 'token', data )
|
|
43
|
+
response[:device_secret] ||= '';
|
|
44
|
+
@applicationToken = response[:application_token]
|
|
45
|
+
return response
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
#
|
|
49
|
+
# Retrieve a thing. If it doesn't exist, then add it.
|
|
50
|
+
#
|
|
51
|
+
# @param [Thing] thing The thing to get or add.
|
|
52
|
+
#
|
|
53
|
+
def add_thing( thing )
|
|
54
|
+
data = {
|
|
55
|
+
token: @applicationToken,
|
|
56
|
+
name: thing.name,
|
|
57
|
+
display_name: thing.display_name
|
|
58
|
+
}
|
|
59
|
+
if( thing.product_type.length > 0 )
|
|
60
|
+
data[:product_type] = thing.product_type
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
@httpHelper.post_data( 'thing', data )
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
#
|
|
67
|
+
# Add or update a status item for a thing.
|
|
68
|
+
#
|
|
69
|
+
# @param [Thing] thing The thing to which the status item is attached.
|
|
70
|
+
# @param [Array] statusArray An array of status item updates (name, value, unit).
|
|
71
|
+
#
|
|
72
|
+
def add_status( thing, statusArray )
|
|
73
|
+
data = {
|
|
74
|
+
token: @applicationToken,
|
|
75
|
+
thing_id: thing.id,
|
|
76
|
+
id: 'null',
|
|
77
|
+
status_array: statusArray
|
|
78
|
+
}
|
|
79
|
+
@httpHelper.post_data( 'status', data )
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
#
|
|
83
|
+
# Add a feed message to a thing.
|
|
84
|
+
#
|
|
85
|
+
# @param [Thing] thing The thing to which the feed message is applied.
|
|
86
|
+
# @param [String] category The feed category.
|
|
87
|
+
# @param [String] message The feed message.
|
|
88
|
+
# @param [FeedOption] feedOptions Additional feed options (icon, progress bar, etc)
|
|
89
|
+
#
|
|
90
|
+
def add_feed( thing, category, message, feedOptions )
|
|
91
|
+
data = {
|
|
92
|
+
token: @applicationToken,
|
|
93
|
+
thing_id: thing.id,
|
|
94
|
+
feed_category: category,
|
|
95
|
+
message: message,
|
|
96
|
+
options: feedOptions
|
|
97
|
+
}
|
|
98
|
+
@httpHelper.post_data( 'feed', data )
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
end
|