simplificator-withings 0.6.10 → 0.7.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 +4 -4
- data/.gitignore +24 -0
- data/Gemfile +4 -0
- data/LICENSE +1 -1
- data/README.rdoc +186 -179
- data/lib/withings/base.rb +36 -34
- data/lib/withings/connection.rb +83 -86
- data/lib/withings/user.rb +111 -103
- data/simplificator-withings.gemspec +24 -55
- data/test/helper.rb +2 -6
- data/test/users_test.rb +31 -28
- metadata +52 -24
data/lib/withings/connection.rb
CHANGED
@@ -1,86 +1,83 @@
|
|
1
|
-
# A convenience class for making get requests to WBS API.
|
2
|
-
# It verifies the response and raises ApiError if a call failed.
|
3
|
-
class Withings::Connection
|
4
|
-
include HTTParty
|
5
|
-
if ENV.has_key?('http_proxy')
|
6
|
-
uri = URI.parse(ENV['http_proxy'])
|
7
|
-
http_proxy uri.host, uri.port
|
8
|
-
end
|
9
|
-
|
10
|
-
base_uri 'wbsapi.withings.net'
|
11
|
-
format :json
|
12
|
-
|
13
|
-
def initialize(user)
|
14
|
-
@user = user
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.get_request(path, token, secret, params)
|
18
|
-
signature = Withings::Connection.sign(base_uri + path, params, token, secret)
|
19
|
-
params.merge!({:oauth_signature => signature})
|
20
|
-
|
21
|
-
response = self.get(path, :query => params)
|
22
|
-
verify_response!(response, path, params)
|
23
|
-
end
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
:
|
36
|
-
:
|
37
|
-
:
|
38
|
-
:
|
39
|
-
:
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
1
|
+
# A convenience class for making get requests to WBS API.
|
2
|
+
# It verifies the response and raises ApiError if a call failed.
|
3
|
+
class Withings::Connection
|
4
|
+
include HTTParty
|
5
|
+
if ENV.has_key?('http_proxy')
|
6
|
+
uri = URI.parse(ENV['http_proxy'])
|
7
|
+
http_proxy uri.host, uri.port
|
8
|
+
end
|
9
|
+
|
10
|
+
base_uri 'wbsapi.withings.net'
|
11
|
+
format :json
|
12
|
+
|
13
|
+
def initialize(user)
|
14
|
+
@user = user
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.get_request(path, token, secret, params)
|
18
|
+
signature = Withings::Connection.sign(base_uri + path, params, token, secret)
|
19
|
+
params.merge!({:oauth_signature => signature})
|
20
|
+
|
21
|
+
response = self.get(path, :query => params)
|
22
|
+
verify_response!(response, path, params)
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_request(path, params)
|
26
|
+
params.merge!({:userid => @user.user_id})
|
27
|
+
self.class.get_request(path, @user.oauth_token, @user.oauth_token_secret, params)
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def self.sign(url, params, token, secret)
|
33
|
+
params.merge!({
|
34
|
+
:oauth_consumer_key => Withings.consumer_key,
|
35
|
+
:oauth_nonce => oauth_nonce,
|
36
|
+
:oauth_signature_method => oauth_signature_method,
|
37
|
+
:oauth_timestamp => oauth_timestamp,
|
38
|
+
:oauth_version => oauth_version,
|
39
|
+
:oauth_token => token
|
40
|
+
})
|
41
|
+
calculate_oauth_signature('GET', url, params, secret)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.oauth_timestamp
|
45
|
+
Time.now.to_i
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.oauth_version
|
49
|
+
'1.0'
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.oauth_signature_method
|
53
|
+
'HMAC-SHA1'
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.oauth_nonce
|
57
|
+
rand(10 ** 30).to_s(16)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.calculate_oauth_signature(method, url, params, oauth_token_secret)
|
61
|
+
# oauth signing is picky with sorting (based on a digest)
|
62
|
+
params = params.to_a.map() do |item|
|
63
|
+
[item.first.to_s, CGI.escape(item.last.to_s)]
|
64
|
+
end.sort
|
65
|
+
|
66
|
+
param_string = params.map() {|key, value| "#{key}=#{value}"}.join('&')
|
67
|
+
base_string = [method, CGI.escape(url), CGI.escape(param_string)].join('&')
|
68
|
+
|
69
|
+
secret = [Withings.consumer_secret, oauth_token_secret].join('&')
|
70
|
+
|
71
|
+
digest = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'), secret, base_string)
|
72
|
+
Base64.encode64(digest).chomp.gsub( /\n/, '' )
|
73
|
+
end
|
74
|
+
|
75
|
+
# Verifies the status code in the JSON response and returns either the body element or raises ApiError
|
76
|
+
def self.verify_response!(response, path, params)
|
77
|
+
if response['status'] == 0
|
78
|
+
response['body'] || response['status']
|
79
|
+
else
|
80
|
+
raise Withings::ApiError.new(response['status'], path, params)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/withings/user.rb
CHANGED
@@ -1,103 +1,111 @@
|
|
1
|
-
class Withings::User
|
2
|
-
attr_reader :short_name, :user_id, :birthdate, :fat_method, :first_name, :last_name, :gender, :oauth_token, :oauth_token_secret
|
3
|
-
|
4
|
-
def self.authenticate(user_id, oauth_token, oauth_token_secret)
|
5
|
-
response = Withings::Connection.get_request('/user', oauth_token, oauth_token_secret, :action => :getbyuserid, :userid => user_id)
|
6
|
-
user_data = response['users'].detect { |item| item['id'] == user_id.to_i }
|
7
|
-
raise Withings::ApiError.new(2555, 'No user found', '') unless user_data
|
8
|
-
Withings::User.new(user_data.merge({:oauth_token => oauth_token, :oauth_token_secret => oauth_token_secret}))
|
9
|
-
end
|
10
|
-
|
11
|
-
# If you create a user yourself, then the only attributes of interest (required for calls to the API) are 'user_id' and 'oauth_token' and 'oauth_token_secret'
|
12
|
-
def initialize(params)
|
13
|
-
params = params.stringify_keys
|
14
|
-
@short_name = params['shortname']
|
15
|
-
@first_name = params['firstname']
|
16
|
-
@last_name = params['lastname']
|
17
|
-
@user_id = params['id'] || params['user_id']
|
18
|
-
@birthdate = Time.at(params['birthdate']) if params['birthdate']
|
19
|
-
@gender = params['gender'] == 0 ? :male : params['gender'] == 1 ? :female : nil
|
20
|
-
@fat_method = params['fatmethod']
|
21
|
-
@oauth_token = params['oauth_token']
|
22
|
-
@oauth_token_secret = params['oauth_token_secret']
|
23
|
-
end
|
24
|
-
|
25
|
-
def subscribe_notification(callback_url, description, device = Withings::SCALE)
|
26
|
-
connection.get_request('/notify', :action => :subscribe, :callbackurl => callback_url, :comment => description, :appli => device)
|
27
|
-
end
|
28
|
-
|
29
|
-
def revoke_notification(callback_url, device = Withings::SCALE)
|
30
|
-
connection.get_request('/notify', :action => :revoke, :callbackurl => callback_url, :appli => device)
|
31
|
-
end
|
32
|
-
|
33
|
-
def describe_notification(callback_url, device = Withings::SCALE)
|
34
|
-
response = connection.get_request('/notify', :action => :get, :callbackurl => callback_url, :appli => device)
|
35
|
-
Withings::NotificationDescription.new(response.merge(:callbackurl => callback_url))
|
36
|
-
end
|
37
|
-
|
38
|
-
# List the notifications for a device (defaults to Withings::SCALE), pass nil to list all devices.
|
39
|
-
def list_notifications(device = Withings::SCALE)
|
40
|
-
options = (device.nil? ? {} : {:appli => device})
|
41
|
-
response = connection.get_request('/notify', options.merge({:action => :list}))
|
42
|
-
response['profiles'].map do |item|
|
43
|
-
Withings::NotificationDescription.new(item)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
|
48
|
-
# List the activity metrics.
|
49
|
-
# Params:
|
50
|
-
# - :date
|
51
|
-
# - :startdateymd
|
52
|
-
# - :enddateymd
|
53
|
-
# Either date OR startdateymd AND enddate need to be supplied.
|
54
|
-
def get_activities(params = {})
|
55
|
-
connection.get_request('/v2/measure', params.merge(:action => :getactivity))
|
56
|
-
end
|
57
|
-
|
58
|
-
|
59
|
-
#
|
60
|
-
#
|
61
|
-
# - :
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
options
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
end
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
1
|
+
class Withings::User
|
2
|
+
attr_reader :short_name, :user_id, :birthdate, :fat_method, :first_name, :last_name, :gender, :oauth_token, :oauth_token_secret
|
3
|
+
|
4
|
+
def self.authenticate(user_id, oauth_token, oauth_token_secret)
|
5
|
+
response = Withings::Connection.get_request('/user', oauth_token, oauth_token_secret, :action => :getbyuserid, :userid => user_id)
|
6
|
+
user_data = response['users'].detect { |item| item['id'] == user_id.to_i }
|
7
|
+
raise Withings::ApiError.new(2555, 'No user found', '') unless user_data
|
8
|
+
Withings::User.new(user_data.merge({:oauth_token => oauth_token, :oauth_token_secret => oauth_token_secret}))
|
9
|
+
end
|
10
|
+
|
11
|
+
# If you create a user yourself, then the only attributes of interest (required for calls to the API) are 'user_id' and 'oauth_token' and 'oauth_token_secret'
|
12
|
+
def initialize(params)
|
13
|
+
params = params.stringify_keys
|
14
|
+
@short_name = params['shortname']
|
15
|
+
@first_name = params['firstname']
|
16
|
+
@last_name = params['lastname']
|
17
|
+
@user_id = params['id'] || params['user_id']
|
18
|
+
@birthdate = Time.at(params['birthdate']) if params['birthdate']
|
19
|
+
@gender = params['gender'] == 0 ? :male : params['gender'] == 1 ? :female : nil
|
20
|
+
@fat_method = params['fatmethod']
|
21
|
+
@oauth_token = params['oauth_token']
|
22
|
+
@oauth_token_secret = params['oauth_token_secret']
|
23
|
+
end
|
24
|
+
|
25
|
+
def subscribe_notification(callback_url, description, device = Withings::SCALE)
|
26
|
+
connection.get_request('/notify', :action => :subscribe, :callbackurl => callback_url, :comment => description, :appli => device)
|
27
|
+
end
|
28
|
+
|
29
|
+
def revoke_notification(callback_url, device = Withings::SCALE)
|
30
|
+
connection.get_request('/notify', :action => :revoke, :callbackurl => callback_url, :appli => device)
|
31
|
+
end
|
32
|
+
|
33
|
+
def describe_notification(callback_url, device = Withings::SCALE)
|
34
|
+
response = connection.get_request('/notify', :action => :get, :callbackurl => callback_url, :appli => device)
|
35
|
+
Withings::NotificationDescription.new(response.merge(:callbackurl => callback_url))
|
36
|
+
end
|
37
|
+
|
38
|
+
# List the notifications for a device (defaults to Withings::SCALE), pass nil to list all devices.
|
39
|
+
def list_notifications(device = Withings::SCALE)
|
40
|
+
options = (device.nil? ? {} : {:appli => device})
|
41
|
+
response = connection.get_request('/notify', options.merge({:action => :list}))
|
42
|
+
response['profiles'].map do |item|
|
43
|
+
Withings::NotificationDescription.new(item)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
# List the activity metrics.
|
49
|
+
# Params:
|
50
|
+
# - :date
|
51
|
+
# - :startdateymd
|
52
|
+
# - :enddateymd
|
53
|
+
# Either date OR startdateymd AND enddate need to be supplied.
|
54
|
+
def get_activities(params = {})
|
55
|
+
connection.get_request('/v2/measure', params.merge(:action => :getactivity))
|
56
|
+
end
|
57
|
+
|
58
|
+
# List the sleep metrics
|
59
|
+
# Params:
|
60
|
+
# - :startdate
|
61
|
+
# - :enddate
|
62
|
+
def get_sleeps(params = {})
|
63
|
+
connection.get_request('/v2/sleep', params.merge(:action => :get))
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
# list measurement groups
|
68
|
+
# The limit and offset parameters are converted to will_paginate style parameters (:per_page, :page)
|
69
|
+
# - :per_page (default: 100)
|
70
|
+
# - :page (default: 1)
|
71
|
+
# - :category (default: empty)
|
72
|
+
# - :measurement_type (default: empty)
|
73
|
+
# - :start_at (default: empty)
|
74
|
+
# - :end_at (default: empty)
|
75
|
+
# - :last_udpated_at (default: empty)
|
76
|
+
# - :device (default: empty)
|
77
|
+
# Parameters are described in WBS api
|
78
|
+
def measurement_groups(params = {})
|
79
|
+
params = params.stringify_keys
|
80
|
+
options = {:limit => 100, :offset => 0}
|
81
|
+
options[:limit] = params['per_page'] if params.has_key?('per_page')
|
82
|
+
options[:offset] = ((params['page'] || 1) - 1) * options[:limit]
|
83
|
+
options[:category] = params['category'] if params.has_key?('category')
|
84
|
+
options[:meastype] = params['measurement_type'] if params.has_key?('measurement_type')
|
85
|
+
options[:startdate] = params['start_at'].to_i if params['start_at']
|
86
|
+
options[:enddate] = params['end_at'].to_i if params['end_at']
|
87
|
+
options[:lastupdate] = params['last_updated_at'].to_i if params['last_updated_at']
|
88
|
+
options[:devtype] = params['device'] if params['device']
|
89
|
+
response = connection.get_request('/measure', options.merge(:action => :getmeas))
|
90
|
+
response['measuregrps'].map do |group|
|
91
|
+
Withings::MeasurementGroup.new(group)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def to_s
|
96
|
+
"[User #{short_name} / #{:user_id}]"
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
protected
|
101
|
+
|
102
|
+
def devices_bitmask(*devices)
|
103
|
+
devices = [Withings::SCALE, Withings::BLOOD_PRESSURE_MONITOR] if Array(devices).empty?
|
104
|
+
devices.inject('|'.to_sym)
|
105
|
+
end
|
106
|
+
|
107
|
+
def connection
|
108
|
+
@connection ||= Withings::Connection.new(self)
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
@@ -1,59 +1,28 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
|
3
|
-
Gem::Specification.new do |
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
"lib/withings/notification_description.rb",
|
27
|
-
"lib/withings/user.rb",
|
28
|
-
"simplificator-withings.gemspec",
|
29
|
-
]
|
30
|
-
s.homepage = %q{http://github.com/simplificator/simplificator-withings}
|
31
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
32
|
-
s.require_paths = ["lib"]
|
33
|
-
s.rubygems_version = %q{1.3.8}
|
34
|
-
s.summary = %q{API implementation for withings.com}
|
35
|
-
s.test_files = [
|
36
|
-
"test/helper.rb",
|
37
|
-
"test/users_test.rb",
|
38
|
-
]
|
39
|
-
|
40
|
-
if s.respond_to? :specification_version then
|
41
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
42
|
-
s.specification_version = 3
|
43
|
-
|
44
|
-
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
45
|
-
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
46
|
-
s.add_development_dependency(%q<mocha>, [">= 0"])
|
47
|
-
s.add_runtime_dependency(%q<httparty>, [">= 0"])
|
48
|
-
else
|
49
|
-
s.add_dependency(%q<shoulda>, [">= 0"])
|
50
|
-
s.add_dependency(%q<mocha>, [">= 0"])
|
51
|
-
s.add_dependency(%q<httparty>, [">= 0"])
|
52
|
-
end
|
53
|
-
else
|
54
|
-
s.add_dependency(%q<shoulda>, [">= 0"])
|
55
|
-
s.add_dependency(%q<mocha>, [">= 0"])
|
56
|
-
s.add_dependency(%q<httparty>, [">= 0"])
|
57
|
-
end
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = %q{simplificator-withings}
|
5
|
+
spec.version = "0.7.0"
|
6
|
+
spec.authors = ["simplificator", "jmaddi", 'invernizzi']
|
7
|
+
spec.date = %q{2011-04-18}
|
8
|
+
spec.description = %q{A withings API implementation in ruby. Created for the evita project at evita.ch}
|
9
|
+
spec.summary = %q{API implementation for withings.com}
|
10
|
+
spec.email = %q{info@simplificator.com}
|
11
|
+
|
12
|
+
spec.homepage = %q{http://github.com/simplificator/simplificator-withings}
|
13
|
+
|
14
|
+
spec.files = `git ls-files -z`.split("\x0")
|
15
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
16
|
+
spec.require_paths = ["lib"]
|
17
|
+
spec.license = "Sea LICENSE"
|
18
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
19
|
+
spec.add_development_dependency "rake", "~> 10.3"
|
20
|
+
spec.add_development_dependency "minitest", "~> 5.4"
|
21
|
+
|
22
|
+
spec.add_development_dependency "mocha", "~> 1.1"
|
23
|
+
|
24
|
+
|
25
|
+
spec.add_dependency 'httparty', "~> 0.13"
|
58
26
|
end
|
59
27
|
|
28
|
+
|