garmin-api-handler
Version:
Unofficial handler for Garmin connect API
250 lines (224 loc) • 7.02 kB
JavaScript
;
var CookieApi = require('cookie-api-handler');
var FormData = require('form-data');
var restApiHandler = require('rest-api-handler');
var garminConnect = require('garmin-connect');
var GarminApiException = require('./exceptions/GarminApiException.js');
var responseDecoder = require('./helpers/responseDecoder.js');
var Activity = require('./models/Activity.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var CookieApi__default = /*#__PURE__*/_interopDefaultLegacy(CookieApi);
var FormData__default = /*#__PURE__*/_interopDefaultLegacy(FormData);
class GarminApi extends CookieApi__default["default"] {
constructor(session) {
super('https://connect.garmin.com/modern/proxy', [new restApiHandler.DefaultResponseProcessor(GarminApiException, responseDecoder)], {
'nk': 'NT',
'Content-Type': 'application/json',
'dnt': '1',
'origin': 'https://connect.garmin.com'
});
this.session = session;
this.addCookies({
_gid: 'X'
});
}
setSession(session) {
this.session = session;
this.addCookies({
SESSIONID: session || ''
});
return this;
}
getSession() {
return this.session;
}
async login(email, password) {
const GCClient = new garminConnect.GarminConnect();
await GCClient.login(email, password);
const {
cookies
} = GCClient.client.cookies._jar.toJSON();
const session = cookies.find(item => item.key === 'SESSIONID' && item.domain === 'connect.garmin.com').value;
const __cflb = cookies.find(item => item.key === '__cflb' && item.domain === 'connect.garmin.com').value;
this.setSession(session);
this.addCookies({
__cflb
});
return session; // does not pass clouflare protection
/*
// get ticket from login form
const { data } = await this.post(
'https://sso.garmin.com/sso/login?service=https%3A%2F%2Fconnect.garmin.com%2Fmodern%2F',
{
username: email,
password,
embed: false,
},
CookieApi.FORMATS.URL_ENCODED,
{
'Content-Type': 'application/x-www-form-urlencoded',
'Referer': 'https://sso.garmin.com/sso/signin',
},
);
const ticket = /ticket=(([A-Za-z]|-|\d)*)/g.exec(data);
// if ticket is no present, there is some error in html
if (!ticket) {
const errorMessage = /<div id="status" class="error">([A-Za-z]| |\.)*<\/div>/g.exec(data);
throw new Error(errorMessage ? errorMessage[1] : 'Error in Login. Cannot find ticket.');
}
// use ticket
try {
await this.request(`?${ticket[0]}`, 'GET', {
redirect: 'manual',
});
// eslint-disable-next-line no-empty
} catch {}
// this will load cookies
await this.get('https://connect.garmin.com/modern/');
const cookies = this.getCookies();
if (!cookies) {
throw new Error('Login failed. Cookies were not loaded.');
}
this.setSession(cookies.SESSIONID);
return cookies.SESSIONID; */
}
async getActivity(id) {
const {
data
} = await this.get(`activity-service/activity/${id}`);
return Activity.fromApi(data);
}
async getPoints(id) {
const {
data
} = await this.get(`activity-service/activity/${id}/details`);
return data;
}
async getActivityGpx(id) {
const {
data
} = await this.get(`download-service/export/gpx/activity/${id}`);
return data;
}
async getActivityFile(id) {
const {
data
} = await this.get(`download-service/files/activity/${id}`);
return data;
}
async getActivities(filter) {
const {
startDate,
endDate
} = filter;
const {
data
} = await this.get('activitylist-service/activities/search/activities', { ...filter,
...(startDate ? {
starDate: startDate.toSQLDate()
} : {}),
...(endDate ? {
endDate: endDate.toSQLDate()
} : {})
});
return data.map(activity => {
return Activity.fromListApi(activity);
});
}
logWeight(date, kg) {
return this.post('weight-service/user-weight', {
gmtTimestamp: date.setZone('UTC').toISO({
includeOffset: false
}),
dateTimestamp: date.toISO({
includeOffset: false
}),
unitKey: 'kg',
value: kg
});
}
async upload(fileContent, format) {
const form = new FormData__default["default"]();
form.append('file', fileContent, {
filename: `activity.${format}`,
contentType: 'application/octet-stream'
});
const headers = this.getDefaultHeaders();
this.setDefaultHeaders({
cookie: headers.cookie,
nk: headers.nk
});
const response = await this.request(`upload-service/upload/.${format}`, 'POST', {
body: form
});
this.setDefaultHeaders(headers);
return response.data.detailedImportResult.successes[0].internalId;
}
async uploadGpx(gpx) {
return this.upload(gpx, 'gpx');
}
async createActivity(activity) {
return this.activityUpdater(activity);
}
async updateActivity(activity) {
return this.activityUpdater(activity);
}
async activityUpdater(activity) {
const distance = activity.getDistance();
const averageHR = activity.getAvgHeartRate();
const maxHR = activity.getMaxHeartRate();
const {
data
} = await this.post(`activity-service/activity/${activity.getId() || ''}`, {
activityName: activity.getTitle() || activity.getTypeId(),
activityTypeDTO: {
typeKey: activity.getTypeId()
},
summaryDTO: {
duration: activity.getDuration().as('seconds'),
startTimeLocal: `${activity.getStart().toISO({
includeOffset: false,
suppressMilliseconds: true
})}.0`,
...(averageHR != null ? {
averageHR
} : {}),
...(maxHR != null ? {
maxHR
} : {}),
...(distance != null ? {
distance: distance.toNumber('m')
} : {})
},
eventTypeDTO: {
typeKey: activity.getCategory()
},
timeZoneUnitDTO: {
unitKey: 'Europe/Prague'
},
description: activity.getNotes(),
...(activity.getId() ? {
activityId: activity.getId()
} : {})
}, CookieApi__default["default"].FORMATS.JSON, { ...(activity.getId() ? {
'x-http-method-override': 'PUT'
} : {})
});
return activity.getId() ? activity : Activity.fromApi(data);
}
async addGear(activityId, gear) {
const {
data
} = await this.post(`gear-service/gear/link/${gear}/activity/${activityId}`, {}, CookieApi__default["default"].FORMATS.JSON, {
'x-http-method-override': 'PUT'
});
return data;
}
async getGears(activityId) {
const {
data
} = await this.get(`gear-service/gear/filterGear?activityId=${activityId}`);
return data;
}
}
module.exports = GarminApi;