A program to read a Phidget IR sensor and log pull-ups with Fitbit's API
1require "oauth"
2require "json"
3
4class Fitbit < Loggerish
5 def self.args
6 [
7 [ "--fitbit", "-F", GetoptLong::NO_ARGUMENT,
8 "log to a Fitbit account (will walk through setup)" ],
9 ]
10 end
11
12 def after_initialize
13 if @enabled = !!@parent.config["fitbit"]
14 @parent.vputs "enabling Fitbit logging module"
15
16 verify_keys
17 end
18 end
19
20 def log_pullup!(time)
21 # TODO: update this to log to the user's custom tracker instead of an
22 # activity, because activities don't allow custom units and logging pullups
23 # in miles doesn't make any sense
24 json = self.oauth_request("/1/user/-/activities.json", :post, {
25 "activityName" => "Pullup",
26 "manualCalories" => 1,
27 "startTime" => time.strftime("%H:%M"),
28 "durationMillis" => 1000,
29 "date" => time.strftime("%Y-%m-%d"),
30 })
31
32 begin
33 h = JSON.parse(json)
34 if (id = h["activityLog"]["activityId"].to_i) != 0
35 @parent.vputs "logged pullup on fitbit (id #{id})"
36 else
37 raise "no activity id"
38 end
39
40 rescue => e
41 @parent.eputs "error from fitbit (#{e.message}): " << json.inspect
42 end
43 end
44
45 def oauth_consumer
46 OAuth::Consumer.new(@parent.config["fitbit_oauth_key"],
47 @parent.config["fitbit_oauth_secret"],
48 { :site => "http://api.fitbit.com", :http_method => :get })
49 end
50
51 def oauth_request(req, method = :get, post_data = nil)
52 begin
53 Timeout.timeout(20) do
54 at = OAuth::AccessToken.new(oauth_consumer,
55 @parent.config["fitbit_token"], @parent.config["fitbit_secret"])
56
57 if method == :get
58 res = at.get(req, { "Accept-Language" => "en_US" })
59 elsif method == :post
60 res = at.post(req, post_data, { "Accept-Language" => "en_US" })
61 else
62 raise "what kind of method is #{method}?"
63 end
64
65 if res.class.superclass != Net::HTTPSuccess
66 raise res.class.to_s
67 end
68
69 return res.body
70 end
71 rescue Timeout::Error => e
72 @parent.eputs "timed out talking to Fitbit: #{e.message}"
73 rescue StandardError => e
74 @parent.eputs "error talking to Fitbit: #{e.message}"
75 end
76 end
77
78 def verify_keys
79 while @parent.config["fitbit_oauth_key"].to_s == ""
80 puts "No Fitbit OAuth key found. Register an application at",
81 "https://dev.fitbit.com/apps/new with PIN authentication and enter ",
82 "the consumer key and secret here.",
83 ""
84
85 print "OAuth consumer key: "
86 @parent.config["fitbit_oauth_key"] = STDIN.gets.strip
87 print "OAuth consumer secret: "
88 @parent.config["fitbit_oauth_secret"] = STDIN.gets.strip
89
90 # this will be invalid now anyway
91 @parent.config["fitbit_token"] = ""
92
93 puts ""
94 end
95
96 while @parent.config["fitbit_token"].to_s == ""
97 request_token = oauth_consumer.get_request_token
98
99 puts "No Fitbit token found. Authorize your Fitbit account at ",
100 request_token.authorize_url,
101 ""
102
103 print "Enter the PIN received: "
104 pin = STDIN.gets.strip
105
106 access_token = request_token.get_access_token(:oauth_verifier => pin)
107 if !access_token
108 raise "couldn't get access token from pin"
109 end
110
111 @parent.config["fitbit_token"] = access_token.token
112 @parent.config["fitbit_secret"] = access_token.secret
113
114 @parent.config.save!
115
116 puts ""
117 end
118 end
119end