fitbit-to-graphite 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/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,20 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ fitbit-to-graphite (0.1.0)
5
+ choice
6
+ fitgem
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ choice (0.1.6)
12
+ fitgem (0.8.0)
13
+ oauth
14
+ oauth (0.4.7)
15
+
16
+ PLATFORMS
17
+ ruby
18
+
19
+ DEPENDENCIES
20
+ fitbit-to-graphite!
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2013 Daniel Schauenberg
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1,45 @@
1
+ # fitbit-to-graphite
2
+
3
+ ## Synopsis
4
+ Record sleep data from fitbit to graphite. That's it.
5
+
6
+ ## Installation
7
+ You can get the script from rubygems:
8
+
9
+ gem install fitbit-to-graphite
10
+
11
+ ## Setup
12
+ fitbit-to-graphite reads its config file at `~/.fitgem.yml` which at least
13
+ needs the following settings:
14
+ ```
15
+ ---
16
+ :oauth:
17
+ :consumer_key: thekey
18
+ :consumer_secret: thesecret
19
+ ```
20
+ Fill in the key and secret from the app you created for it and on first run it
21
+ should register the OAuth data.
22
+
23
+ ## Usage
24
+ ```
25
+ % fitbit-to-graphite.rb --help
26
+ Usage: fitbit-to-graphite.rb [-hpnv]
27
+
28
+ Specific options:
29
+ -h, --host=HOST The hostname or ip of the host graphite is running on
30
+ -p, --port=PORT The port graphite is listening on
31
+ -n, --namespace=NAMESPACE The graphite metric path to store data in
32
+
33
+ Common options:
34
+ --help Show this message
35
+ -v, --version Show version
36
+ --debug run in debug mode
37
+ --jawbone send jawbone compatible data
38
+ ```
39
+
40
+ ## How to contribute
41
+ 1. Fork the repo
42
+ 2. Hack away
43
+ 3. Push the branch up to GitHub
44
+ 4. Send a pull request
45
+
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
@@ -0,0 +1,174 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # simple script to import your sleep data from Fitbit into Graphite
5
+ #
6
+
7
+ require 'date'
8
+ require 'yaml'
9
+
10
+ require 'choice'
11
+ require 'fitgem'
12
+
13
+ PROGRAM_VERSION = "0.1.0"
14
+ SLEEP_STATE_TYPES = ['deep', 'light', 'awake']
15
+ # this is just to be compatible with jawbone data
16
+ JAWBONE_SLEEP_STATES = { 'awake' => 1, 'light' => 2, 'deep' => 3 }
17
+
18
+ Choice.options do
19
+ header ''
20
+ header 'Specific options:'
21
+
22
+ option :host do
23
+ short '-h'
24
+ long '--host=HOST'
25
+ desc 'The hostname or ip of the host graphite is running on'
26
+ default '127.0.0.1'
27
+ end
28
+
29
+ option :port do
30
+ short '-p'
31
+ long '--port=PORT'
32
+ desc 'The port graphite is listening on'
33
+ cast Integer
34
+ default 2003
35
+ end
36
+
37
+ option :namespace do
38
+ short '-n'
39
+ long '--namespace=NAMESPACE'
40
+ desc 'The graphite metric path to store data in'
41
+ end
42
+
43
+ separator ''
44
+ separator 'Common options: '
45
+
46
+ option :help do
47
+ long '--help'
48
+ desc 'Show this message'
49
+ end
50
+
51
+ option :version do
52
+ short '-v'
53
+ long '--version'
54
+ desc 'Show version'
55
+ action do
56
+ puts "#{$0} Fitbit sleep data importer v#{PROGRAM_VERSION}"
57
+ exit
58
+ end
59
+ end
60
+
61
+ option :debug do
62
+ long '--debug'
63
+ desc 'run in debug mode'
64
+ end
65
+
66
+ option :jawbone do
67
+ long '--jawbone'
68
+ desc 'send jawbone compatible data'
69
+ end
70
+ end
71
+
72
+ def client_setup
73
+ # Load the existing yml config
74
+ config = begin
75
+ Fitgem::Client.symbolize_keys(YAML.load(File.open(File.join(ENV['HOME'], ".fitgem.yml"))))
76
+ rescue ArgumentError => e
77
+ puts "Could not parse YAML: #{e.message}"
78
+ exit
79
+ end
80
+
81
+ client = Fitgem::Client.new(config[:oauth])
82
+
83
+ # With the token and secret, we will try to use them
84
+ # to reconstitute a usable Fitgem::Client
85
+ if config[:oauth][:token] && config[:oauth][:secret]
86
+ begin
87
+ access_token = client.reconnect(config[:oauth][:token], config[:oauth][:secret])
88
+ rescue Exception => e
89
+ puts "Error: Could not reconnect Fitgem::Client due to invalid keys in .fitgem.yml"
90
+ exit
91
+ end
92
+ # Without the secret and token, initialize the Fitgem::Client
93
+ # and send the user to login and get a verifier token
94
+ else
95
+ request_token = client.request_token
96
+ token = request_token.token
97
+ secret = request_token.secret
98
+
99
+ puts "Go to http://www.fitbit.com/oauth/authorize?oauth_token=#{token} and then enter the verifier code below"
100
+ verifier = gets.chomp
101
+
102
+ begin
103
+ access_token = client.authorize(token, secret, { :oauth_verifier => verifier })
104
+ rescue Exception => e
105
+ puts "Error: Could not authorize Fitgem::Client with supplied oauth verifier"
106
+ exit
107
+ end
108
+
109
+ user_id = client.user_info['user']['encodedId']
110
+
111
+ config[:oauth].merge!(:token => access_token.token, :secret => access_token.secret, :user_id => user_id)
112
+
113
+ # Write the whole oauth token set back to the config file
114
+ File.open(File.join(ENV['HOME'], ".fitgem.yml"), "w") {|f| f.write(config.to_yaml) }
115
+ end
116
+
117
+ return client
118
+ end
119
+
120
+ def extract_data(client, &block)
121
+ if block.nil?
122
+ puts "No block given."
123
+ return
124
+ end
125
+ user_info = client.user_info
126
+ user_timezone = user_info['user']['timezone']
127
+ today = Date.today
128
+ all_sleep_data = client.sleep_on_date(today)['sleep']
129
+ if all_sleep_data.nil?
130
+ puts "API rate limit potentially exceeded."
131
+ return
132
+ end
133
+ sleep_data = []
134
+ sleep_summary = nil
135
+ all_sleep_data.each do |potential_log|
136
+ if potential_log['isMainSleep']
137
+ sleep_data = potential_log['minuteData']
138
+ sleep_summary = potential_log
139
+ end
140
+ end
141
+ createdate = DateTime.strptime("#{sleep_summary['startTime']}#{user_timezone}",'%Y-%m-%dT%H:%M:%S.%L%z').to_time.to_i
142
+ msg = ""
143
+ msg << "#{Choice[:namespace]}.summary.awakenings #{sleep_summary['awakeningsCount']} #{createdate}\n"
144
+ msg << "#{Choice[:namespace]}.summary.quality #{sleep_summary['efficiency']} #{createdate}\n"
145
+ msg << "#{Choice[:namespace]}.summary.deep_minutes #{sleep_summary['minutesAsleep']} #{createdate}\n"
146
+ msg << "#{Choice[:namespace]}.summary.light_minutes #{sleep_summary['minutesAwake']} #{createdate}\n"
147
+ sleep_data.each do |data|
148
+ d = DateTime.strptime("#{today.year}-#{today.month}-#{today.day}T#{data['dateTime']}#{user_timezone}",'%Y-%m-%dT%H:%M:%S%z')
149
+ state_name = SLEEP_STATE_TYPES[ data['value'].to_i - 1 ]
150
+ value = Choice[:jawbone] == true ? JAWBONE_SLEEP_STATES[state_name] : data['value']
151
+ msg << "#{Choice[:namespace]}.details.#{state_name} #{value} #{d.to_time.to_i}\n"
152
+ end
153
+ yield msg
154
+ end
155
+
156
+ def send_to_graphite(client)
157
+ extract_data(client) do |msg|
158
+ socket = TCPSocket.open(Choice[:host], Choice[:port])
159
+ socket.write(msg)
160
+ end
161
+ end
162
+
163
+ def print_sleep_data(client)
164
+ extract_data(client) {|msg| puts msg}
165
+ end
166
+
167
+
168
+ # main
169
+ client = client_setup
170
+ if Choice[:debug]
171
+ print_sleep_data(client)
172
+ else
173
+ send_to_graphite(client)
174
+ end
@@ -0,0 +1,16 @@
1
+ Gem::Specification.new do |gem|
2
+ gem.name = 'fitbit-to-graphite'
3
+ gem.version = '0.1.0'
4
+ gem.authors = ["Daniel Schauenberg"]
5
+ gem.email = 'd@unwiredcouch.com'
6
+ gem.homepage = 'https://github.com/mrtazz/fitbit-to-graphite'
7
+ gem.summary = "script to import fitbit sleep data into graphite"
8
+ gem.description = "script to import fitbit sleep data into graphite"
9
+ gem.license = 'MIT'
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.name = "fitbit-to-graphite"
13
+
14
+ gem.add_runtime_dependency "fitgem"
15
+ gem.add_runtime_dependency "choice"
16
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fitbit-to-graphite
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Daniel Schauenberg
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-10-13 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: fitgem
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: choice
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: script to import fitbit sleep data into graphite
47
+ email: d@unwiredcouch.com
48
+ executables: []
49
+ extensions: []
50
+ extra_rdoc_files: []
51
+ files:
52
+ - Gemfile
53
+ - Gemfile.lock
54
+ - LICENSE
55
+ - README.md
56
+ - Rakefile
57
+ - bin/fitbit-to-graphite.rb
58
+ - fitbit-to-graphite.gemspec
59
+ homepage: https://github.com/mrtazz/fitbit-to-graphite
60
+ licenses:
61
+ - MIT
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ! '>='
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubyforge_project:
80
+ rubygems_version: 1.8.25
81
+ signing_key:
82
+ specification_version: 3
83
+ summary: script to import fitbit sleep data into graphite
84
+ test_files: []
85
+ has_rdoc: