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.
@@ -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&param2=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
@@ -0,0 +1,15 @@
1
+ require_relative ( 'baseTask' )
2
+
3
+ class ThingTask < BaseTask
4
+
5
+ def initialize( webService, thing )
6
+ super( webService, thing, true )
7
+ @web = webService
8
+ @thing = @thing
9
+ end
10
+
11
+ def do_task_work
12
+ @web.add_thing( @thing )
13
+ end
14
+
15
+ end
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ module Thingdom
2
+ VERSION = "0.1.0"
3
+ end
@@ -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