// gc-export-daily-hr // v4 (2025-11-14) function gcExportDailyHeartRate() { let loc = window.location.href let connectURL = "https://connect.garmin.com"; //https://connect.garmin.com/modern/heart-rate/2025-03-10/0 let dailyURL = "https://connect.garmin.com/modern/heart-rate/" if (loc.indexOf(connectURL) != 0 || typeof jQuery === "undefined" || !localStorage.token) { alert( `You must be logged into Garmin Connect to run this script. Log into ${connectURL} and try again.` ); return; } // Garmin Connect uses jQuery, so it's available for this script jQuery("#_gc-daily_hr_modal").remove(); _gcExportDailyHeartRate(); function _gcExportDailyHeartRate() { let today = new Date(); if (loc.indexOf(dailyURL) == 0) { let todayStr = loc.replace(dailyURL, ""); const dateRegExp = /^(\d\d\d\d)-(\d\d)-(\d\d)/; const match = todayStr.match(dateRegExp); if (match && match.length !== 0) { today = new Date(match[1], match[2]-1, match[3]); } } let defaultStartDate = formatDate(today); let startDate = promptDate( `Export Garmin Connect Daily Heart Rate Enter date to export (YYYY-MM-DD): `, defaultStartDate ) if (!startDate) { return; } startDate = formatDate(startDate); let xhr = new XMLHttpRequest(); const token = document.querySelector('meta[name="csrf-token"]') if (token) { xhr.open('GET', `https://connect.garmin.com/gc-api/wellness-service/wellness/dailyHeartRate?date=${startDate}`); xhr.setRequestHeader('connect-csrf-token', token.content); } else { xhr.open('GET', `https://connect.garmin.com/wellness-service/wellness/dailyHeartRate?date=${startDate}`); xhr.setRequestHeader('Authorization', 'Bearer '+JSON.parse(localStorage.token).access_token); xhr.setRequestHeader("NK", "NT") xhr.setRequestHeader('di-backend', 'connectapi.garmin.com') } xhr.onload = function () { if (xhr.status !== 200) { alert(`Error exporting data: ${xhr.status} ${xhr.statusText}\n\nMake sure you are logged into Garmin Connect and try again.`) return; } let obj = JSON.parse(xhr.response) addDialog(obj, startDate) }; xhr.onerror = function(error) { alert(`Error exporting data: ${error}\n\nnMake sure you are logged into Garmin Connect and try again.`) } xhr.send() } function formatDate(date) { let d = new Date(date), month = '' + (d.getMonth() + 1), day = '' + d.getDate(), year = d.getFullYear(); if (month.length < 2) month = '0' + month; if (day.length < 2) day = '0' + day; return [year, month, day].join('-'); } function formatDateAndTime(date) { let d = new Date(date), month = '' + (d.getMonth() + 1), day = '' + d.getDate(), year = d.getFullYear(); if (month.length < 2) month = '0' + month; if (day.length < 2) day = '0' + day; return `${[year, month, day].join('-')} ${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}:${d.getSeconds().toString().padStart(2, '0')}`; } function promptDate(str, def) { while (true) { const val = prompt(str, def); if (!val) { return val; } const dateRegExp = /^(\d\d\d\d)-(\d\d)-(\d\d)$/; const match = val.match(dateRegExp); if (!match || match.length == 0) { continue; } let d; d = new Date(match[1], match[2]-1, match[3], 0, 0, 0); // console.log(d) return d; } } function getCSV(data) { let csv = `HR Date/Time,HR Value,Summary Description,Summary Value\n`; csv += `,,Date,${data.calendarDate}\n`; csv += `,,Min HR,${data.minHeartRate}\n`; csv += `,,Max HR,${data.maxHeartRate}\n`; csv += `,,Resting HR,${data.restingHeartRate}\n`; csv += `,,Resting HR (7-day average),${data.lastSevenDaysAvgRestingHeartRate}\n`; csv += `,,,\n`; for (let i = 0; i < data.heartRateValues.length; i++) { var val = data.heartRateValues[i]; var d = new Date(0); d.setUTCSeconds(val[0] / 1000); if (val[1] === null || val[1] === undefined) { csv += `${formatDateAndTime(d)},,,\n`; } else { csv += `${formatDateAndTime(d)},${val[1]},,\n`; } } return csv; } function addDialog(data, startDate) { addCSS(); jQuery("#_gc-daily_hr_modal").remove(); const output = JSON.stringify(data, null, 2); if (data.heartRateValues === null) { alert(`No heart rate data found for ${data.calendarDate}`); return; } const csv = getCSV(data); jQuery('body').append(`