Skip to content

Instantly share code, notes, and snippets.

@yuanying
Created May 7, 2012 07:33
Show Gist options
  • Save yuanying/2626497 to your computer and use it in GitHub Desktop.
Save yuanying/2626497 to your computer and use it in GitHub Desktop.
Syncronize from Garmin Connect activity to Runkeeper
#!/usr/bin/env ruby -wKU
# Syncronize from Garmin Connect to Runkeeper
require 'rubygems'
require 'runkeeper'
require 'rss/1.0'
require 'rss/2.0'
require 'rss/dublincore'
require 'open-uri'
require 'uri'
require 'cgi'
require 'optparse'
require "rexml/document"
require 'time'
runkeeper_client_id = nil
runkeeper_client_secret = nil
runkeeper_access_token = nil
garmin_username = nil
proxy_host = nil
proxy_port = nil
opts = OptionParser.new
opts.on('-c', '--runkeeper-client-id CLIENT_ID') { |v| runkeeper_client_id = v }
opts.on('-s', '--runkeeper-client-secret CLIENT_SECRET') { |v| runkeeper_client_secret = v }
opts.on('-a', '--runkeeper-access-token ACCESS_TOKEN') { |v| runkeeper_access_token = v }
opts.on('-g', '--garmin-username GARMIN_USERNAME') { |v| garmin_username = v }
opts.on('--proxy-host PROXY_HOST') { |v| proxy_host = v }
opts.on('--proxy-port PROXY_PORT', Integer) { |v| proxy_port = v }
opts.parse!(ARGV)
tmp = '/tmp/last-garmin-connect-to-runkeeper-url.txt'
url = nil
if File.exist?(tmp)
open(tmp) do |io|
url = io.read.chomp
end
end
rss = nil
rss_url = "http://connect.garmin.com/feed/rss/activities?feedname=Garmin#{Time.now.strftime('%Y%m%d%H')}&explore=true&activityType=running&eventType=all&activitySummarySumDistance-unit=kilometer&activitySummarySumDuration-unit=hour&activitySummaryGainElevation-unit=meter&owner=#{garmin_username}&sortField=beginTimestamp"
open(rss_url) do |io|
rss = RSS::Parser.parse(io.read)
end
Runkeeper.configure do |config|
config.client_id = runkeeper_client_id
config.client_secret = runkeeper_client_secret
end
if proxy_host && proxy_port
require 'runkeeper/connection'
class Runkeeper
class Connection
http_proxy proxy_host, proxy_port
end
end
end
class Runkeeper
class Activity
attr_accessor :heart_rate
alias old_optional_attrs optional_attrs
def optional_attrs
h = {}
[
:total_distance,
:average_heart_rate,
:total_calories,
:heart_rate,
:notes,
:path,
:post_to_facebook,
:post_to_twitter
].each do |method_name|
v = send(method_name)
h[method_name] = v if v
end
h
end
end
end
class Garmin2Runkeeper
attr_accessor :runkeeper
def initialize runkeeper
@runkeeper = runkeeper
end
def post activity_id
type = 'Running'
start_time = nil
total_distance = 0
duration = nil
# average_heart_rate = 0
heart_rate = []
total_calories = 0
notes = nil
path = []
open("http://connect.garmin.com/proxy/activity-service-1.1/gpx/activity/#{activity_id}?full=true") do |gpx|
doc = REXML::Document.new gpx
title = doc.elements['/gpx/trk/name'] ? doc.elements['/gpx/trk/name'].text : 'Untitle'
desc = doc.elements['/gpx/trk/desc'] ? doc.elements['/gpx/trk/desc'].text : ''
notes = "#{title}, #{desc}"
end
open("http://connect.garmin.com/proxy/activity-service-1.1/tcx/activity/#{activity_id}?full=true") do |tcx|
doc = REXML::Document.new tcx
doc.elements.each('/TrainingCenterDatabase/Activities/Activity/Lap') do |lap|
if distance = lap.elements['DistanceMeters']
distance = distance.text.to_i
total_distance += distance
end
if calories = lap.elements['Calories']
calories = calories.text.to_i
total_calories += calories
end
trigger_method = trigger_method.text.downcase if trigger_method = lap.elements['TriggerMethod']
lap.elements.each('Track/Trackpoint') do |tp|
if time = tp.elements['Time']
time = Time.parse(time.text)
start_time = time unless start_time
duration = time.to_i - start_time.to_i
latitude = tp.elements['Position/LatitudeDegrees']
longitude = tp.elements['Position/LongitudeDegrees']
altitude = tp.elements['AltitudeMeters']
trigger_method = trigger_method || 'manual'
if latitude && longitude && altitude
wgs84 = {}
wgs84[:timestamp] = duration
wgs84[:latitude] = latitude.text
wgs84[:longitude] = longitude.text
wgs84[:altitude] = altitude.text
wgs84[:type] = trigger_method
path << wgs84
end
if hr_value = tp.elements['HeartRateBpm/Value']
hr_value = hr_value.text.to_i
hr = {}
hr[:timestamp] = duration
hr[:heart_rate] = hr_value
heart_rate << hr
end
end
end
end
end
activity = runkeeper.new_activity(
type: type,
start_time: start_time.localtime,
duration: duration,
heart_rate: heart_rate,
path: path,
total_distance: total_distance,
total_calories: total_calories,
notes: notes,
post_to_facebook: true,
post_to_twitter: true
)
activity.save
end
end
user = Runkeeper.new(runkeeper_access_token)
synchronizer = Garmin2Runkeeper.new(user)
urls = []
rss.items.each do |item|
if item.link == url
break
end
urls << item.link
end
urls.reverse.each do |url|
activity_id = url.split('/').last
synchronizer.post(activity_id)
open(tmp, 'w') do |io|
io.write url
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment