class Fitbyte::Client
Constants
- ACTIVITY_INTRADAY_RESOURCES
- ACTIVITY_RESOURCES
- BODY_RESOURCES
- FOOD_RESOURCES
- PERIODS
- SLEEP_RESOURCES
Attributes
Public Class Methods
# File lib/fitbyte/client.rb, line 19 def initialize(opts) missing_args = [:client_id, :client_secret] - opts.keys raise Fitbyte::InvalidArgumentError, "Required arguments: #{missing_args.join(', ')}" if missing_args.size > 0 %w(client_id client_secret redirect_uri site_url authorize_url token_url unit_system locale scope api_version snake_case_keys symbolize_keys).each do |attr| instance_variable_set("@#{attr}", (opts[attr.to_sym] || Fitbyte.send(attr))) end @client = OAuth2::Client.new(@client_id, @client_secret, site: @site_url, authorize_url: @authorize_url, token_url: @token_url) restore_token(opts[:refresh_token]) if opts[:refresh_token] end
Public Instance Methods
Returns the details of a specific activity in the Fitbit activities database in the format requested.
If activity has levels, also returns a list of activity level details.
# File lib/fitbyte/activities.rb, line 65 def activity(activity_id) get("activities/#{activity_id}.json") end
# File lib/fitbyte/activities.rb, line 104 def activity_intraday_time_series(resource, opts={}) date = opts[:date] || Date.today detail_level = opts[:detail_level] start_time = opts[:start_time] end_time = opts[:end_time] unless ACTIVITY_INTRADAY_RESOURCES.include?(resource) raise Fitbyte::InvalidArgumentError, "Invalid resource: \"#{resource}\". Please provide one of the following: #{ACTIVITY_RESOURCES}." end if [date, detail_level].any?(&:nil?) raise Fitbyte::InvalidArgumentError, 'A date and detail_level are required.' end unless %(1min 15min).include? detail_level raise Fitbyte::InvalidArgumentError, "Invalid detail_level: \"#{detail_level}\". Please provide one of the following: \"1min\" or \"15min\"." end if (start_time || end_time) && !(start_time && end_time) raise Fitbyte::InvalidArgumentError, 'Both start_time and end_time are required if time is being specified.' end if (start_time && end_time) get("user/-/activities/#{resource}/date/#{format_date(date)}/1d/#{detail_level}/time/#{format_time(start_time)}/#{format_time(end_time)}.json") else get("user/-/activities/#{resource}/date/#{format_date(date)}/1d/#{detail_level}.json") end end
Parameters¶ ↑
-
:beforeDate
- the date; formatted in yyyy-MM-ddTHH:mm:ss -
:afterDate
- the date; formatted in yyyy-MM-ddTHH:mm:ss -
:sort
- the sort order of entries by date (asc or desc) -
:offset
- the offset number of entries -
:limit
- the max of the number of entries returned (max: 100)
# File lib/fitbyte/activities.rb, line 58 def activity_logs_list(opts={}) get("user/#{user_id}/activities/list.json", opts) end
# File lib/fitbyte/activities.rb, line 78 def activity_time_series(resource, opts={}) start_date = opts[:start_date] end_date = opts[:end_date] || Date.today period = opts[:period] unless ACTIVITY_RESOURCES.include?(resource) raise Fitbyte::InvalidArgumentError, "Invalid resource: \"#{resource}\". Please provide one of the following: #{ACTIVITY_RESOURCES}." end if [start_date, period].none? raise Fitbyte::InvalidArgumentError, 'A start_date or period is required.' end if period && !PERIODS.include?(period) raise Fitbyte::InvalidArgumentError, "Invalid period: \"#{period}\". Please provide one of the following: #{PERIODS}." end if period result = get("user/#{user_id}/activities/#{resource}/date/#{format_date(end_date)}/#{period}.json", opts) else result = get("user/#{user_id}/activities/#{resource}/date/#{format_date(start_date)}/#{format_date(end_date)}.json", opts) end # remove root key from response result.values[0] end
POST Parameters¶ ↑
-
:time
- time of day that the alarm vibrates with a UTC timezone offset, e.g. 07:15-08:00 -
:enabled
- boolean; if false, alarm does not vibrate until enabled is set to true -
:recurring
- boolean; if false, the alarm is a single event -
:weekDays
- comma separated list of days of the week on which the alarm vibrates (MONDAY,TUESDAY)
# File lib/fitbyte/alarms.rb, line 23 def add_alarm(tracker_id, opts={}) post("user/#{user_id}/devices/tracker/#{tracker_id}/alarms.json", opts) end
Adds the activity with the given ID to user's list of favorite activities.
# File lib/fitbyte/activities.rb, line 155 def add_favorite_activity(activity_id) post("user/#{user_id}/activities/favorite/#{activity_id}.json") end
Returns a list of the set alarms connected to a user's account.
# File lib/fitbyte/alarms.rb, line 8 def alarms(tracker_id, opts={}) get("user/#{user_id}/devices/tracker/#{tracker_id}/alarms.json", opts) end
Gets a tree of all valid Fitbit public activities from
the activities catalog as well as private custom activities the user created.
# File lib/fitbyte/activities.rb, line 44 def all_activities(opts={}) get('activities.json', opts) end
# File lib/fitbyte/client.rb, line 58 def auth_header { 'Authorization' => ('Basic ' + Base64.encode64(@client_id + ':' + @client_secret)) } end
# File lib/fitbyte/client.rb, line 34 def auth_url @client.auth_code.authorize_url(redirect_uri: @redirect_uri, scope: @scope) end
# File lib/fitbyte/user.rb, line 7 def badges(opts={}) get("user/#{user_id}/badges.json", opts) end
Retrieves a user's current body fat percentage goal.
# File lib/fitbyte/goals.rb, line 14 def body_fat_goal(opts={}) get("user/-/body/log/fat/goal.json", opts) end
# File lib/fitbyte/body.rb, line 9 def body_fat_logs(date=Date.today, opts={}) get("user/-/body/log/fat/date/#{format_date(date)}.json", opts) end
# File lib/fitbyte/body.rb, line 13 def body_time_series(resource, opts={}) start_date = opts[:start_date] end_date = opts[:end_date] || Date.today period = opts[:period] unless BODY_RESOURCES.include?(resource) raise Fitbyte::InvalidArgumentError, "Invalid resource: \"#{resource}\". Please provide one of the following: #{BODY_RESOURCES}." end if [period, start_date].none? raise Fitbyte::InvalidArgumentError, 'A start_date or period is required.' end if period && !PERIODS.include?(period) raise Fitbyte::InvalidArgumentError, "Invalid period: \"#{period}\". Please provide one of the following: #{PERIODS}." end if period result = get("user/#{user_id}/body/#{resource}/date/#{format_date(end_date)}/#{period}.json", opts) else result = get("user/#{user_id}/body/#{resource}/date/#{format_date(start_date)}/#{format_date(end_date)}.json", opts) end # remove root key from response result.values[0] end
POST Parameters¶ ↑
-
:caloriesOut
- calories output goal value; integer -
:activeMinutes
- active minutes goal value; integer -
:floors
- floor goal value; integer -
:distance
- distance goal value; X.XX or integer -
:steps
- steps goal value; integer
# File lib/fitbyte/goals.rb, line 43 def create_or_update_daily_goals(opts={}) post("user/#{user_id}/activities/goals/daily.json", opts) end
POST Parameters¶ ↑
-
:caloriesOut
- calories output goal value; integer -
:activeMinutes
- active minutes goal value; integer -
:floors
- floor goal value; integer -
:distance
- distance goal value; X.XX or integer -
:steps
- steps goal value; integer
# File lib/fitbyte/goals.rb, line 57 def create_or_update_weekly_goals(opts={}) post("user/#{user_id}/activities/goals/weekly.json", opts) end
Retrieves a summary and list of a user's activities and activity log entries for a given day
in the format requested using units in the unit system which corresponds to the Accept-Language header provided.
# File lib/fitbyte/activities.rb, line 20 def daily_activity_summary(date=Date.today, opts={}) get("user/#{user_id}/activities/date/#{format_date(date)}.json", opts) end
Retrieves a user's current daily activity goals.
# File lib/fitbyte/goals.rb, line 20 def daily_goals(opts={}) get("user/#{user_id}/activities/goals/daily.json", opts) end
# File lib/fitbyte/helpers/utils.rb, line 42 def deep_keys_to_camel_case!(object) deep_transform_keys!(object) { |key| to_camel_case(key, lower: true) } end
# File lib/fitbyte/helpers/utils.rb, line 38 def deep_keys_to_snake_case!(object) deep_transform_keys!(object) { |key| to_snake_case(key, replace_dashes: true) } end
# File lib/fitbyte/helpers/utils.rb, line 46 def deep_symbolize_keys!(object) deep_transform_keys!(object) { |key| key.to_sym rescue key } end
Inspired by ActiveSupport's implementation
# File lib/fitbyte/helpers/utils.rb, line 51 def deep_transform_keys!(object, &block) case object when Hash object.keys.each do |key| value = object.delete(key) object[yield(key)] = deep_transform_keys!(value) { |key| yield(key) } end object when Array object.map! { |e| deep_transform_keys!(e) { |key| yield(key) } } else object end end
# File lib/fitbyte/client.rb, line 82 def delete(path, opts={}) response = token.delete(("#{@api_version}/" + path), headers: request_headers).response object = MultiJson.load(response.body) unless response.status == 204 process_keys!(object, opts) end
Deletes a user's activity log entry with the given ID.
# File lib/fitbyte/activities.rb, line 164 def delete_activity(activity_log_id) delete("user/#{user_id}/activities/#{activity_log_id}.json") end
Deletes the user's device alarm entry with the given ID for a given device.
# File lib/fitbyte/alarms.rb, line 48 def delete_alarm(tracker_id, alarm_id, opts={}) delete("user/#{user_id}/devices/tracker/#{tracker_id}/alarms/#{alarm_id}.json", opts) end
# File lib/fitbyte/body.rb, line 51 def delete_body_fat_log(body_fat_log_id, opts={}) delete("user/#{user_id}/body/log/fat/#{body_fat_log_id}.json", opts) end
Removes the activity with the given ID from a user's list of favorite activities.
# File lib/fitbyte/activities.rb, line 170 def delete_favorite_activity(activity_id) delete("user/#{user_id}/activities/favorite/#{activity_id}.json") end
# File lib/fitbyte/body.rb, line 43 def delete_weight_log(weight_log_id, opts={}) delete("user/#{user_id}/body/log/weight/#{weight_log_id}.json", opts) end
# File lib/fitbyte/devices.rb, line 3 def devices(opts={}) get("user/#{user_id}/devices.json", opts) end
Returns a list of a user's favorite activities.
# File lib/fitbyte/activities.rb, line 37 def favorite_activities(opts={}) get("user/#{user_id}/activities/favorite.json", opts) end
# File lib/fitbyte/food.rb, line 17 def favorite_foods(opts={}) get("user/#{user_id}/foods/log/favorite.json", opts) end
# File lib/fitbyte/food.rb, line 21 def food_goals(opts={}) get("user/#{user_id}/foods/log/goal.json", opts) end
# File lib/fitbyte/food.rb, line 5 def food_logs(date=Date.today, opts={}) get("user/#{user_id}/foods/log/date/#{format_date(date)}.json", opts) end
# File lib/fitbyte/food.rb, line 25 def food_time_series(resource, opts={}) start_date = opts[:start_date] end_date = opts[:end_date] || Date.today period = opts[:period] unless FOOD_RESOURCES.include?(resource) raise Fitbyte::InvalidArgumentError, "Invalid resource: \"#{resource}\". Please provide one of the following: #{FOOD_RESOURCES}." end if [period, start_date].none? raise Fitbyte::InvalidArgumentError, 'A start_date or period is required.' end if period && !PERIODS.include?(period) raise Fitbyte::InvalidArgumentError, "Invalid period: \"#{period}\". Please provide one of the following: #{PERIODS}." end if period result = get("user/#{user_id}/foods/log/#{resource}/date/#{format_date(end_date)}/#{period}.json", opts) else result = get("user/#{user_id}/foods/log/#{resource}/date/#{format_date(start_date)}/#{format_date(end_date)}.json", opts) end # remove root key from response result.values[0] end
# File lib/fitbyte/helpers/utils.rb, line 6 def format_date(date) if [Date, Time, DateTime].include?(date.class) date.strftime("%Y-%m-%d") elsif date.is_a? String if date =~ /\d{4}\-\d{2}\-\d{2}/ date else raise Fitbyte::InvalidArgumentError, "Invalid argument [\"#{date}\"] - string must follow yyyy-MM-dd format." end else raise Fitbyte::InvalidArgumentError, "Invalid type [#{date.class}] - provide a Date/Time/DateTime or a String(yyyy-MM-dd format)." end end
# File lib/fitbyte/helpers/utils.rb, line 34 def format_scope(scope) scope.is_a?(Array) ? scope.join(' ') : scope end
# File lib/fitbyte/helpers/utils.rb, line 20 def format_time(time) if [Time, DateTime].include?(time.class) time.strftime('%H:%M') elsif time.is_a? String if time =~ /\d{2}\:\d{2}/ time else raise Fitbyte::InvalidArgumentError, "Invalid argument [\"#{time}\"] - string must follow HH:mm format." end else raise Fitbyte::InvalidArgumentError, "Invalid type [#{time.class}] - provide a Time/DateTime or a String(HH:mm format)." end end
Retrieves a list of a user's frequent activities in the format requested using units
in the unit system which corresponds to the Accept-Language header provided.
# File lib/fitbyte/activities.rb, line 27 def frequent_activities(opts={}) get("user/#{user_id}/activities/frequent.json", opts) end
# File lib/fitbyte/food.rb, line 13 def frequent_foods(opts={}) get("user/#{user_id}/foods/log/frequent.json", opts) end
# File lib/fitbyte/friends.rb, line 3 def friends(opts={}) get("user/#{user_id}/friends.json", opts) end
# File lib/fitbyte/friends.rb, line 7 def friends_leaderboard(opts={}) get("user/#{user_id}/friends/leaderboard.json", opts) end
# File lib/fitbyte/client.rb, line 70 def get(path, opts={}) response = token.get(("#{@api_version}/" + path), headers: request_headers).response object = MultiJson.load(response.body) unless response.status == 204 process_keys!(object, opts) end
# File lib/fitbyte/client.rb, line 38 def get_token(auth_code) @token = @client.auth_code.get_token(auth_code, redirect_uri: @redirect_uri, headers: auth_header) @user_id = @token.params['user_id'] return @token end
# File lib/fitbyte/heart_rate.rb, line 25 def heart_rate_intraday_time_series(opts={}) date = opts[:date] || Date.today detail_level = opts[:detail_level] start_time = opts[:start_time] end_time = opts[:end_time] if [date, detail_level].any?(&:nil?) raise Fitbyte::InvalidArgumentError, 'A date and detail_level are required.' end unless %(1sec 1min).include? detail_level raise Fitbyte::InvalidArgumentError, "Invalid detail_level: \"#{detail_level}\". Please provide one of the following: \"1sec\" or \"1min\"." end if (start_time || end_time) && !(start_time && end_time) raise Fitbyte::InvalidArgumentError, 'Both start_time and end_time are required if time is being specified.' end if (start_time && end_time) get("user/-/activities/heart/date/#{format_date(date)}/1d/#{detail_level}/time/#{format_time(start_time)}/#{format_time(end_time)}.json") else get("user/-/activities/heart/date/#{format_date(date)}/1d/#{detail_level}.json") end end
# File lib/fitbyte/heart_rate.rb, line 3 def heart_rate_time_series(opts={}) start_date = opts[:start_date] end_date = opts[:end_date] || Date.today period = opts[:period] if [period, start_date].none? raise Fitbyte::InvalidArgumentError, 'A start_date or period is required.' end if period && !PERIODS.include?(period) raise Fitbyte::InvalidArgumentError, "Invalid period: \"#{period}\". Please provide one of the following: #{PERIODS}." end if period result = get("user/#{user_id}/activities/heart/date/#{format_date(end_date)}/#{period}.json", opts) else result = get("user/#{user_id}/activities/heart/date/#{format_date(start_date)}/#{format_date(end_date)}.json", opts) end # remove root key from response result.values[0] end
Retrieves the user's activity statistics in the format requested using units
in the unit system which corresponds to the Accept-Language header provided. Activity statistics includes Lifetime and Best achievement values from the My Achievements tile on the website dashboard.
# File lib/fitbyte/activities.rb, line 74 def lifetime_stats(opts={}) get("user/#{user_id}/activities.json", opts) end
POST Parameters¶ ↑
-
:activityId
- activity id -
:activityName
- custom activity name. Either activity ID or activityName must be provided -
:manualCalories
- calories burned, specified manually. Required with activityName, otherwise optional -
:startTime
- activity start time; formatted in HH:mm:ss -
:durationMillis
- duration in milliseconds -
:date
- log entry date; formatted in yyyy-MM-dd -
:distance
- distance; required for logging directory activity; formatted in X.XX -
:distanceUnit
- distance measurement unit
# File lib/fitbyte/activities.rb, line 149 def log_activity(opts) post("user/#{user_id}/activities.json", opts) end
# File lib/fitbyte/body.rb, line 47 def log_body_fat(opts) post("user/#{user_id}/body/log/fat.json", opts) end
# File lib/fitbyte/body.rb, line 39 def log_weight(opts) post("user/#{user_id}/body/log/weight.json", opts) end
# File lib/fitbyte/client.rb, line 76 def post(path, opts={}) response = token.post(("#{@api_version}/" + path), body: deep_keys_to_camel_case!(opts), headers: request_headers).response object = MultiJson.load(response.body) unless response.status == 204 process_keys!(object, opts) end
# File lib/fitbyte/client.rb, line 88 def process_keys!(object, opts={}) deep_keys_to_snake_case!(object) if (opts[:snake_case_keys] || snake_case_keys) deep_symbolize_keys!(object) if (opts[:symbolize_keys] || symbolize_keys) return object end
# File lib/fitbyte/user.rb, line 3 def profile(opts={}) get("user/#{user_id}/profile.json", opts) end
# File lib/fitbyte/activities.rb, line 31 def recent_activities(opts={}) get("user/#{user_id}/activities/recent.json", opts) end
# File lib/fitbyte/food.rb, line 9 def recent_foods(opts={}) get("user/#{user_id}/foods/log/recent.json", opts) end
# File lib/fitbyte/client.rb, line 54 def refresh_token @token = @token.refresh!(headers: auth_header) end
# File lib/fitbyte/client.rb, line 62 def request_headers { 'User-Agent' => "fitbyte-#{Fitbyte::VERSION} gem (#{Fitbyte::REPO_URL})", 'Accept-Language' => @unit_system, 'Accept-Locale' => @locale } end
# File lib/fitbyte/client.rb, line 44 def restore_token(refresh_token) @token = OAuth2::AccessToken.from_hash(@client, refresh_token: refresh_token).refresh!(headers: auth_header) @user_id = @token.params['user_id'] return @token end
# File lib/fitbyte/sleep.rb, line 6 def sleep_logs(date=Date.today, opts={}) get("user/#{user_id}/sleep/date/#{format_date(date)}.json", opts) end
# File lib/fitbyte/sleep.rb, line 10 def sleep_time_series(resource, opts={}) start_date = opts[:start_date] end_date = opts[:end_date] || Date.today period = opts[:period] unless SLEEP_RESOURCES.include?(resource) raise Fitbyte::InvalidArgumentError, "Invalid resource: \"#{resource}\". Please provide one of the following: #{SLEEP_RESOURCES}." end if [period, start_date].none? raise Fitbyte::InvalidArgumentError, 'A start_date or period is required.' end if period && !PERIODS.include?(period) raise Fitbyte::InvalidArgumentError, "Invalid period: \"#{period}\". Please provide one of the following: #{PERIODS}." end if period result = get("user/#{user_id}/activities/#{resource}/date/#{format_date(end_date)}/#{period}.json", opts) else result = get("user/#{user_id}/activities/#{resource}/date/#{format_date(start_date)}/#{format_date(end_date)}.json", opts) end # remove root key from response result.values[0] end
# File lib/fitbyte/helpers/utils.rb, line 75 def to_camel_case(word, opts={}) string = word.to_s return string if string.match(/[A-Z]|[a-z]([A-Z0-9]*[a-z][a-z0-9]*[A-Z]|[a-z0-9]*[A-Z][A-Z0-9]*[a-z])[A-Za-z0-9]*/) string = word.to_s.split('_').collect(&:capitalize).join string.gsub!(/^\w{1}/) { |word| word.downcase } if opts[:lower] return string end
# File lib/fitbyte/helpers/utils.rb, line 66 def to_snake_case(word, opts={}) string = word.to_s.dup return string.downcase if string.match(/\A[A-Z]+\z/) string.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2') string.gsub!(/([a-z])([A-Z])/, '\1_\2') string.gsub!('-', '_') if opts[:replace_dashes] string.downcase end
# File lib/fitbyte/client.rb, line 50 def token @token.expired? ? refresh_token : @token end
POST Parameters¶ ↑
-
:time
- time of day that the alarm vibrates with a UTC timezone offset, e.g. 07:15-08:00 -
:enabled
- boolean; if false, alarm does not vibrate until enabled is set to true -
:recurring
- boolean; if false, the alarm is a single event -
:weekDays
- comma separated list of days of the week on which the alarm vibrates (MONDAY,TUESDAY) -
:snoozeLength
- integer; minutes between alarms -
:snoozeCount
- integer; maximum snooze count -
:label
- string; label for alarm -
:vibe
- vibe pattern; only one value for now (DEFAULT)
# File lib/fitbyte/alarms.rb, line 39 def update_alarm(tracker_id, alarm_id, opts={}) post("user/#{user_id}/devices/tracker/#{tracker_id}/alarms/#{alarm_id}.json", opts) end
# File lib/fitbyte/user.rb, line 11 def update_profile(opts) post("user/#{user_id}/profile.json", opts) end
# File lib/fitbyte/water.rb, line 3 def water_logs(date=Date.today, opts={}) get("user/#{user_id}/foods/log/water/date/#{format_date(date)}.json", opts) end
Retrieves a user's current weekly activity goals.
# File lib/fitbyte/goals.rb, line 26 def weekly_goals(opts={}) get("user/#{user_id}/activities/goals/weekly.json", opts) end
Retrieves a user's current weight goal.
# File lib/fitbyte/goals.rb, line 8 def weight_goal(opts={}) get("user/-/body/log/weight/goal.json", opts) end
# File lib/fitbyte/body.rb, line 5 def weight_logs(date=Date.today, opts={}) get("user/-/body/log/weight/date/#{format_date(date)}.json", opts) end