Giter Club home page Giter Club logo

garmin-connect's People

Contributors

04nd01 avatar cdillinger avatar cketcham avatar florianpasteur avatar gooin avatar gregegan avatar loic294 avatar martvdmoosdijk avatar pythe1337n avatar ray0711 avatar steffen-4s1 avatar tihawk avatar wscourge avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

garmin-connect's Issues

Fails and gives the link to recover the password

It seems that sometimes it fails and gives the link to recover the password (and forces you to change it since a temporary one arrives, it has already happened to me about 5 times), check the timeOut issue or something because it clicks where it does not touch sometimes..
image
Thnks

Garmin Signin API changed

403 Forbidden Response

I've been able to retrieve activities for many months but this morning I get a 403 response when I call GCClient.getActivities.

The body does not contain an array of activities, it contains this error object:
{ clientMessage: 'Reference Error ID in error logs for further information', errorId: 'e6b09f8b-b515-4307-bd10-2e07c065f515', error: 'WebApplicationException' }

The stacktrace is:

Unhandled rejection StatusCodeError: 403 - "{\"clientMessage\":\"Reference Error ID in error logs for further information\",\"errorId\":\"48588b72-47c0-4ddb-bfc7-7356464e41a7\",\"error\":\"WebApplicationException\"}" at new StatusCodeError (D:\foo\node_modules\request-promise-core\lib\errors.js:32:15) at plumbing.callback (D:\foo\node_modules\request-promise-core\lib\plumbing.js:104:33) at Request.RP$callback [as _callback] (D:\foo\node_modules\request-promise-core\lib\plumbing.js:46:31) at self.callback (D:\foo\node_modules\request\request.js:185:22) at onRequestComplete (D:\foo\node_modules\cloudscraper\index.js:629:3) at onRequestResponse (D:\foo\node_modules\cloudscraper\index.js:207:5) at Request.<anonymous> (D:\foo\node_modules\cloudscraper\index.js:149:7) at Object.onceWrapper (node:events:628:26) at Request.emit (node:events:513:28) at Request.<anonymous> (D:\foo\node_modules\request\request.js:1154:10) at Request.emit (node:events:513:28) at IncomingMessage.<anonymous> (D:\foo\node_modules\request\request.js:1076:12) at Object.onceWrapper (node:events:627:28) at IncomingMessage.emit (node:events:525:35) at endReadableNT (node:internal/streams/readable:1359:12) at process.processTicksAndRejections (node:internal/process/task_queues:82:21)

I ran an npm update and changed to using @garmin/fitsdk (rather than the deprecated @garmin-fit/sdk) but the 403 error still exists.

I have no issues logging into Garmin Connect with the same credentials.

Updating user account data

Hey, I struggle trying to update my user account at https://www.garmin.com/account/profile/ and I figured I'll kindly ask for your help. The actual form is behind the "Edit" button on the linked URL.

It's on different subdomain (www) but bases on the same SSO auth when using the browser, and I think that's the problems' root cause, but I don't know how to properly address it. What I've tried:

const { GarminConnect } = require('garmin-connect');
const credentials = {
  email: "...",
  password: "...",
}

const GCClient = new GarminConnect(credentials);

GCClient.login(credentials.email, credentials.password)
  .then(async () => {
    await GCClient.put("https://www.garmin.com/account/api/user", {
      user_full_name: "...",
      user_email: credentials.email,
      user_country: "GB",
      user_language: "en-US"
      // _csrf: "4riLASKZ-hLBG6nQv4AJl_O0ByhU4ylxsen8",
    })
  })
  .catch(console.error)

I would like to specifically update the user_country. The error I receive is HTTP 403 Forbidden.

Thanks for this great package and keep up the good work.

Failed to restore session (user hash doesn't match)

Restoring session not working. I use garmin.config.json file

My code :

const { GarminConnect } = require("garmin-connect");
const { writeFileSync, readFileSync, existsSync } = require("fs");
const GCClient = new GarminConnect();

async function main() {
  if (existsSync("userInfo.json")) {
    const userInfoFile = readFileSync("userInfo.json", "utf8");
    console.log("Restore session");
    await GCClient.restoreOrLogin(JSON.parse(userInfoFile));
  } else {
    console.log("Login");
    await GCClient.login();
  }
  const userInfo = await GCClient.getUserInfo();
  console.log(userInfo);
  writeFileSync("userInfo.json", JSON.stringify(userInfo), "utf8");
}

main();

The error :

Error: Unable to restore session due to: Error: Unable to restore session, user hash do not match
    at GarminConnect.restore (F:\Projects\Websites\Dashboard\Garmin Test\node_modules\garmin-connect\dist\garmin\GarminConnect.js:142:19)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async main (F:\Projects\Websites\Dashboard\Garmin Test\index.js:11:5)

Weather

Hello, is it possible to receive weather data from activity?

About the userprofile interface

Hi, thanks for your masterpiece.
I found on connect.garmin.com the userprofile interface is like https://connect.garmin.com/modern/proxy/userprofile-service/userprofile/personal-information/userhash rather than that in this project /currentuser-service/user/info. Could you tell me why?
image

Looking forward for your reply

PUT request

Hello, can you add PUT request back? I can't update activities anymore :( I have tried add it back after 1.6.0 update but without any results...

TypeError: this.client.put is not a function

Export UrlClass

Hi, @Pythe1337N

Thank you for this library.

There's an obvious use case to use custom requests, and IMHO it would make sense to export UrlClass for that purpose. I am using UrlClass, because you, as a maintainer, know better which URLs are possible. As an example, I had no idea of the GC_API URL until I looked at the source code.

Right now I am importing UrlClass directly like this
import gcUrl from 'garmin-connect/dist/garmin/UrlClass.js';
which is very flaky.

Thank you for all your time and effort!

P.S. If you would prefer it, I can create PR for this

Custom request required payment ?

I update the package to new version 1.6.1 to use custom request with the get method but I have this error message in my console
"Error: ERROR: (402), Payment Required"
We have to pay something to use the custom request ?
Thanks

Add optional untyped/type parameters to getActivities method

Hi, @Pythe1337N

underlying getActivities API can support multiple filters, besides offset or limit. For example, very useful parameters are startDate and endDate, activityType.

I propose that, if possible and make sense, add an optional hashmap parameter to the getActivities call and insert those into request params.

Thank you for all your time and effort!

P.S. If you would prefer it, I can create PR for this

Recommendation for developers: Use ChatGPT for generating types & readme docs

To generate types
Drop in the entire api response from garmin connect, and ask chat gpt to generate you types for it! so fast.

To generate readme docs

Drop this example in, along with your function code and ask chat gpt to create developer readme docs in the same format as this for your function.

### `getDailyWeight(date?: Date): Promise<number>`

Retrieves the daily weight and converts it from grams to pounds.

#### Parameters:

-   `date` (Date, optional): Date of information requested. Defaults to the current date.

#### Returns:

-   `Promise<number>`: A Promise that resolves to the daily weight converted from grams to pounds.

#### Throws:

-   `Error`: If valid daily weight data cannot be found for the specified date.

#### Example:

```js
const weightInPounds = await instance.getDailyWeight(new Date('2023-12-25'));
console.log(`Daily weight on 2023-12-25: ${weightInPounds.toFixed(1)} lbs`);

Cannot find name WeightData/UpdateWeight/WaterIntake/GolfSummary/GolfScorecard/HeartRate

Hello,
after last update (17.01.2024) we are faced with 6 errors of missing types in node_modules/garmin-connect/dist/garmin/GarminConnect.d.ts
node_modules/garmin-connect/dist/garmin/GarminConnect.d.ts:60:46 - error TS2304: Cannot find name 'WeightData'.
60 getDailyWeightData(date?: Date): Promise;
~~~~~~~~~~
node_modules/garmin-connect/dist/garmin/GarminConnect.d.ts:63:82 - error TS2304: Cannot find name 'UpdateWeight'.
63 updateWeight(date: Date | undefined, lbs: number, timezone: string): Promise;
~~~~~~~~~~~~
node_modules/garmin-connect/dist/garmin/GarminConnect.d.ts:64:82 - error TS2304: Cannot find name 'WaterIntake'.
64 updateHydrationLogOunces(date: Date | undefined, valueInOz: number): Promise;
~~~~~~~~~~~
node_modules/garmin-connect/dist/garmin/GarminConnect.d.ts:65:31 - error TS2304: Cannot find name 'GolfSummary'.
65 getGolfSummary(): Promise;
~~~~~~~~~~~
node_modules/garmin-connect/dist/garmin/GarminConnect.d.ts:66:52 - error TS2304: Cannot find name 'GolfScorecard'.
66 getGolfScorecard(scorecardId: number): Promise;
~~~~~~~~~~~~~
node_modules/garmin-connect/dist/garmin/GarminConnect.d.ts:67:40 - error TS2304: Cannot find name 'HeartRate'.
67 getHeartRate(date?: Date): Promise;

Typescript version is same as garmin's.
We use es2017, but changing to ES5 didn't solve issue.

Could you help to solve it ?

setBodyWeight error

When invoking setBodyWeight I get the following

TypeError: Cannot destructure property 'body' of '(intermediate value)' as it is undefined.
at CFClient.putJson (F:\dotnet.garmin.connect-main\dotnet.garmin.connect-main\Garmin.Connect.NodeJS\node_modules\garmin-connect\dist\common\CFClient.js:135:17)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async GarminConnect.put (F:\dotnet.garmin.connect-main\dotnet.garmin.connect-main\Garmin.Connect.NodeJS\node_modules\garmin-connect\dist\garmin\GarminConnect.js:431:26)
at async main (F:\dotnet.garmin.connect-main\dotnet.garmin.connect-main\Garmin.Connect.NodeJS\app.js:13:5)
PS F:\dotnet.garmin.connect-main\dotnet.garmin.connect-main\Garmin.Connect.NodeJS> node app.js
F:\dotnet.garmin.connect-main\dotnet.garmin.connect-main\Garmin.Connect.NodeJS\node_modules\garmin-connect\dist\common\CFClient.js:135
const { body } = await this.scraper(options);
^

I've installed the package using npm. Calling code is pretty simple, see below. Any assistance greatly appriciated.

const { GarminConnect } = require('garmin-connect');

// Has to be run in an async function to be able to use the await keyword
const main = async () => {
    // Create a new Garmin Connect Client
    const GCClient = new GarminConnect();

    // Uses credentials from garmin.config.json or uses supplied params
    await GCClient.login('myrealemail', 'myrealpassword');

    // Get user info
    await GCClient.setBodyWeight(80.0);

    // Log info to make sure signin was successful
    console.log(info);
};

// Run the code
main();

Run example code

Info return pure html code.
{

<style data-href="/styles.3bfbd3dd9436b814ee1a.css">body,html{height:auto;font:400 14px/22px Open Sans,Helvetica Neue,Helvetica,Arial,sans-serif;color:#222;background-color:#efefef;margin:0}a,a:active,a:hover,a:visited{text-decoration:none;color:inherit}#truste-consent-track{display:block}#truste-consent-content{display:flex;align-items:center;justify-content:center;flex-wrap:wrap;width:100%;box-sizing:border-box}#truste-consent-text{margin:0 1rem 0 0}#truste-consent-buttons{display:flex;flex-direction:row;margin-top:1px!important;margin-left:10px!important}#truste-consent-button,#truste-show-consent{font-family:Open Sans,HelveticaNeue,HelveticaNeueu,Arial,sans-serif;font-size:14px;color:#fff;background-color:#4a4a4a;cursor:pointer;padding:5px 20px;margin:12px 0;border-radius:4px;border:none}#truste-show-consent{margin-left:1rem}#truste-consent-button:hover,#truste-show-consent:hover{background-color:#4a4a4a}#truste-cookie-button{color:#fff;margin:1rem;line-height:2}@media (max-width:1024px){#truste-consent-content{flex-direction:column;flex-wrap:nowrap}#truste-consent-text{margin:20px 20px 10px;text-align:center;width:unset!important}#truste-consent-buttons{display:block;text-align:center}#truste-consent-button,#truste-show-consent{padding-top:10px;padding-bottom:10px;width:100%;margin:5px 0}#truste-cookie-button{display:inline-block;margin:auto;padding:15px}#truste-show-consent{margin-left:0}}.truste_overlay{background:#fff!important;opacity:.8!important}.truste_box_overlay{background:none!important}.truste_box_overlay_inner{border:3px solid #e8e8e8}</style><title data-react-helmet="true">Garmin Connect | </title><style type="text/css" id="server-side-jss">.c017 {

height: 60px;
display: flex;
padding: 0 15% 0 15%;
background: #1a1a1a;
justify-content: space-between;
}
.c018 {
top: 3px;
width: 150px;
height: 28px;
display: block;
position: relative;
background: transparent url(/static/[email protected]) no-repeat 0 -23.47619px;
background-size: 236px 271px;
}
.c019 {
float: right;
margin: 0;
list-style: none;
}
.c0110 {
display: inline-block;
margin-right: 25px;
}
.c0110 a {
color: #fff;
font-size: 1em;
font-weight: 300;
}
.c0112 {
width: 0;
height: 0;
display: inline-block;
border-top: 4px solid #fff;
border-left: 4px solid transparent;
margin-left: 5px;
border-right: 4px solid transparent;
}
.c0113 {
width: 0;
height: 0;
display: inline-block;
border-left: 4px solid transparent;
margin-left: 5px;
border-right: 4px solid transparent;
border-bottom: 4px solid #fff;
}
.c0114 {
display: none;
}
.c0115 {
margin: 0;
padding: 0;
}
.c0116 {
font-size: inherit;
font-weight: inherit;
margin-block-end: 0em;
margin-block-start: 0em;
}
.c0117 {
margin-top: 12px;
}
.c0118 {
margin-top: 12px;
}
@media only screen and (max-width: 767px) {
.c018 {
top: -3px;
}
.c0120 {
height: 100%;
}
.c0121 {
height: 100vh;
}
.c017 {
margin: 0;
display: flex;
padding: 0;
flex-flow: wrap;
justify-content: space-between;
}
.c0117 {
display: none;
}
.c0114 {
width: 20px;
margin: 23px 25px;
height: 14px;
display: inline-flex;
background: transparent url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjBweCIgaGVpZ2h0PSIxNHB4IiB2aWV3Qm94PSIwIDAgMjAgMTQiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUzLjIgKDcyNjQzKSAtIGh0dHBzOi8vc2tldGNoYXBwLmNvbSAtLT4KICAgIDx0aXRsZT5pY29uLW1lbnU8L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZyBpZD0iaWNvbi1tZW51IiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgICAgICA8cGF0aCBkPSJNMCwwIEwyMCwwIEwyMCwyIEwwLDIgTDAsMCBaIE0wLDYgTDIwLDYgTDIwLDggTDAsOCBMMCw2IFogTTAsMTIgTDIwLDEyIEwyMCwxNCBMMCwxNCBMMCwxMiBaIiBpZD0iQ29tYmluZWQtU2hhcGUiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgIDwvZz4KPC9zdmc+) no-repeat;
}
.c0122 {
margin: 0;
display: block;
padding: 0;
flex-flow: wrap;
background: #121212;
justify-content: space-between;
}
.c0122 .c018 {
background: transparent url(/static/[email protected]) no-repeat 0 -23.47619px;
background-size: 236px 271px;
}
.c0122 .c0117 {
margin: 5px 0 10px 0;
display: flex;
flex-basis: 100%;
}
.c0122 .c0115 {
width: 100%;
margin: 0;
padding: 0;
display: grid;
}
.c0122 .c0110 {
color: #fff;
margin: 0;
padding: 10px 25px;
border-top: 1px solid #4d4d4d;
}
.c0122 .c0112 {
float: right;
margin-top: 10px;
border-top: 4px solid #fff;
}
.c0122 .c0113 {
float: right;
margin-top: 10px;
border-bottom: 4px solid #fff;
}
.c0122 .c0119 {
width: 80%;
margin-top: 20px;
border-top: 0px;
text-align: center;
justify-self: center;
margin-right: 0;
}
.c0122 .c0114 {
width: 20px;
right: 0;
height: 18px;
display: inline-block;
position: absolute;
background: transparent url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMTZweCIgaGVpZ2h0PSIxNnB4IiB2aWV3Qm94PSIwIDAgMTYgMTYiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUzLjIgKDcyNjQzKSAtIGh0dHBzOi8vc2tldGNoYXBwLmNvbSAtLT4KICAgIDx0aXRsZT5pY29uLWNsb3NlPC90aXRsZT4KICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPgogICAgPGcgaWQ9Imljb24tY2xvc2UiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxwYXRoIGQ9Ik02LjU0NTQ1NDU1LDggTC0xLjU5NzM4ODg5ZS0xMiwxLjQ1NDU0NTQ1IEwxLjQ1NDU0NTQ1LC0xLjY0NDI0MDNlLTEyIEw4LDYuNTQ1NDU0NTUgTDE0LjU0NTQ1NDUsMS42NTIwMTE4NmUtMTIgTDE2LDEuNDU0NTQ1NDUgTDkuNDU0NTQ1NDUsOCBMMTYsMTQuNTQ1NDU0NSBMMTQuNTQ1NDU0NSwxNiBMOCw5LjQ1NDU0NTQ1IEwxLjQ1NDU0NTQ1LDE2IEwtMS42MDI0OTU5MWUtMTIsMTQuNTQ1NDU0NSBMNi41NDU0NTQ1NSw4IFoiIGlkPSJDb21iaW5lZC1TaGFwZSIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgPC9nPgo8L3N2Zz4=) no-repeat ;
}
.c0122 .c0111 {
margin: 0;
padding: 0;
}
.c0122 .c0111 div {
padding: 10px 25px;
}
.c0122 .c0111 ul {
max-height: initial;
}
.c0122 .c0111 li {
color: #000;
padding: 10px 25px;
}
.c0122 .c0110:nth-last-child(2) {
border-bottom: 1px solid #4d4d4d;
}
.c0122 .c0110 a {
color: #fff;
}
.c0118 {
display: inline-flex;
padding: 10px 25px;
flex-basis: 50%;
}
}
.c0136 {
display: inline;
}
.c0136 a {
white-space: nowrap;
}
.c0137 {
left: 0;
width: 100%;
color: #fff;
bottom: 0;
margin: 0 auto;
z-index: 110;
position: fixed;
font-size: 14px;
font-family: 'Open Sans','HelveticaNeue','HelveticaNeueu',Arial,sans-serif;
background-color: #333;
}
.c0123 {
left: 0;
width: 100%;
bottom: 0;
display: flex;
padding: 60px 0;
text-align: center;
flex-direction: column;
justify-content: center;
background-color: #EFEFEF;
}
.c0124 {
font-size: 1.3rem;
font-weight: 200;
padding-bottom: 30px;
}
.c0124 a {
padding: 10px;
border-color: #555555;
border-style: solid;
border-width: 1px;
border-radius: 3px;
}
.c0124 a:visited, .c0124 a:hover, .c0124 a:active {
color: #555555;
}
.c0125 {
flex: 1;
color: #6c6c6c;
box-sizing: border-box;
text-align: left;
font-weight: 300;
padding-left: 20px;
}
.c0126 {
flex: 1;
padding: 0;
box-sizing: border-box;
text-align: right;
}
.c0127 {
color: #6c6c6c;
display: inline-block;
font-weight: 400;
}
.c0127 a:visited, .c0127 a:hover, .c0127 a:active {
color: #6c6c6c;
text-decoration: none;
}
.c0127:not(:first-of-type):before {
width: 5px;
height: 5px;
margin: 0 10px 3px 10px;
content: "";
display: inline-block;
border-radius: 50%;
background-color: #6c6c6c;
}
.c0128 {
display: flex;
align-items: center;
justify-content: center;
}
.c0129 {
width: 24px;
height: 24px;
margin: 15px;
display: inline-block;
}
.c0130 {
width: 24px;
height: 24px;
margin: 15px;
display: inline-block;
}
.c0131 {
width: 24px;
height: 24px;
margin: 15px;
display: inline-block;
}
.c0132 a {
width: 24px;
height: 24px;
display: inline-block;
}
@media screen and (max-width:767px) {
.c0128 {
padding: 0 5px 0 5px;
flex-wrap: wrap;
}
.c0126 {
flex-basis: 100%;
text-align: center;
}
.c0125 {
text-align: center;
padding-left: 0;
}
}
.c0133 {
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjRweCIgaGVpZ2h0PSIyNHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUzLjEgKDcyNjMxKSAtIGh0dHBzOi8vc2tldGNoYXBwLmNvbSAtLT4KICAgIDx0aXRsZT5zb2NpYWwtaW5zdGFncmFtLWljb248L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZyBpZD0ic29jaWFsLWluc3RhZ3JhbS1pY29uIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgICAgICA8ZyBpZD0iaW5zdGFncmFtIj4KICAgICAgICAgICAgPHBvbHlnb24gaWQ9IlBhdGgiIHBvaW50cz0iMCAwIDIzLjQyMTE3NjUgMCAyMy40MjExNzY1IDIzLjQyMTE3NjUgMCAyMy40MjExNzY1Ij48L3BvbHlnb24+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNS45MDk2MzU0LDAgQzE4Ljc3ODM2MTEsMC4wMDUwNjQ2NjQ5MiAxOC44ODk3NTM5LDAuMDI4OTQwOTQyNCAxOS40NTAzMzQ0LDAuMTA5OTc1NTgxIEMyMC42MDY5Mzg3LDAuMjc3MTA5NTIzIDIxLjU3ODM3MDUsMC43Mzg3MTc1NTQgMjIuMzcxODYzMiwxLjQ5Njk3MDI1IEMyMi43OTgwNTU5LDEuODk4OTcyOTUgMjMuMTQ2MjgyOCwyLjM3NjMxODgxIDIzLjM5ODk5MTQsMi45MDQ5NDcwOSBDMjMuNzM1MzM5OCwzLjU4OTQwMDM4IDIzLjkwMDk4MjMsNC4yMzc2Nzc0OSAyMy45NzYyMDg2LDUuMjAxNDEwODcgQzIzLjk5Mjg0NTEsNS40MDgzMzg2MSAyNC4wMDAwNzg0LDguNjk4OTIzNzYgMjQuMDAwMDc4NCwxMS45OTI0MDMgQzI0LjAwMDA3ODQsMTUuMjg0NDM1MiAyMy45OTM1Njg1LDE4LjU4MTUzMjEgMjMuOTc2OTMxOSwxOC43ODg0NTk4IEMyMy45MDI0Mjg5LDE5LjczNjk5OTIgMjMuNzM2Nzg2NCwyMC4zOTAzNDEgMjMuNDExMjg4LDIxLjA2MjQ5NDMgQzIyLjc2NzQ2NTEsMjIuMzk1OTA0MSAyMS41Njg1OTA1LDIzLjM3NzQ5NzUgMjAuMTM0NjA0NCwyMy43NDUzMTk3IEMxOS42MTM4MDcsMjMuODc5ODk1MSAxOS4xNDIxOTYsMjMuOTQzNTY1MiAxOC4zMjk4OTY3LDIzLjk4MTE4ODQgQzE4LjAzOTg0MTUsMjMuOTk2MzgyNCAxNS4wMDkwODk5LDI0IDExLjk3ODMzODQsMjQgQzguOTQ3NTg2NzksMjQgNS45MTgyODE4OSwyMy45OTA1OTQyIDUuNjIwOTkzMzgsMjMuOTc3NTcwOCBDNC45MjAwODY4NywyMy45NDQyODg3IDQuMzczMjQ5NiwyMy44NzQxMDY5IDMuODc5MjE1MzksMjMuNzQ2NzY2OCBDMi40NDgxNzAzNiwyMy4zNzY2NDgyIDEuMjUwODM3MjcsMjIuMzk4NDMyIDAuNjAyNTMxNzI1LDIxLjA2OTcyOTYgQzAuMjcwNTIzMzgsMjAuMzgwOTM1MiAwLjExMzU2MDgyOSwxOS43NTE0Njk3IDAuMDMwMzc3OTEsMTguNzUwODM2NiBDMC4wMDY1MDgwMjg5NSwxOC40NjY0OTE4IDAuMDAwNzIxMzkxMTMxLDE3LjAwMjgwMzcgMC4wMDA3MjEzOTExMzEsMTEuOTk1Mjk3MSBDMC4wMDA3MjEzOTExMzEsMTAuMzA4MDQwMiAtMC4wMDA3MjUyNjgzMjUsOS4wNTA1NTYyMSAwLjAwMDcyMTM5MTEzMSw4LjA5NDc4MTU5IEMwLjAwNTc4NDY5OTIzLDUuMjI4MTgxMjQgMC4wMjg5MzEyNTA1LDUuMTEzODY0NTIgMC4xMTA2Njc1MSw0LjU1MjQxMDI0IEMwLjI3Nzc1NjY3NywzLjM5NjIxOTU5IDAuNzM5MjQxMDQzLDIuNDIzODAzOTMgMS41MDAxODM5MiwxLjYzMjk5MjY3IEMxLjg5NTY4MjgxLDEuMjE2NTg0MjcgMi4zNjI2MjgwNSwwLjg3NDQ5MDc1MiAyLjg3ODg1MDM4LDAuNjIyOTUzNzg1IEMzLjU3NjE0MDI0LDAuMjc2Mzg2IDQuMjE1NTYzNzIsMC4xMTQzMTY3MjIgNS4yNDQxMzg1OSwwLjAyODk0MDk0MjQgQzUuNTI5MTMwNSwwLjAwNTA2NDY2NDkyIDYuOTkzODczMiwwIDEyLjAwMzY1NDksMCBMMTUuOTA5NjM1NCwwIFogTTExLjk5NzE0NDksMy4xMzg2NDUyIEwxMS45OTg1OTE2LDMuMTM4NjQ1MiBMMTEuOTk1Njk4MywzLjEzODY0NTIgTDExLjk5NzE0NDksMy4xMzg2NDUyIEM5LjU5MjA3MzU4LDMuMTM4NjQ1MiA5LjI4OTcyMTc2LDMuMTQ5NDk4MDYgOC4zNDUwNTMxMywzLjE5MjE4NTk1IEM3LjQwMTgzMTE3LDMuMjM1NTk3MzYgNi43NTgwNjc3MSwzLjM4NDY0MzIxIDYuMTk0NTkzODUsMy42MDM4NzA4NSBDNS42MDMzMzQwMSwzLjgyNjUxNjUxIDUuMDY3Njk0MDIsNC4xNzUzOTMwNiA0LjYyNDk2ODM0LDQuNjI2MjA5NjQgQzQuMTc0MzYwMTksNS4wNjg4NDQzOCAzLjgyNTU4MjQ2LDUuNjA0MzcyNzcgMy42MDI5MDM0NCw2LjE5NTUzMjI0IEMzLjM4MzAxMTIsNi43NTkxNTcxIDMuMjMzMjgxOTQsNy40MDM4MTY1OSAzLjE5MTMyODgyLDguMzQ2NTY3NzkgQzMuMTQ4NjUyMzcsOS4yOTIyMTMwOCAzLjEzNzgwMjQyLDkuNTkzOTIyNCAzLjEzNzgwMjQyLDEyLjAwMTA4NTMgQzMuMTM3ODAyNDIsMTQuNDA3NTI0NiAzLjE0ODY1MjM3LDE0LjcwOTIzNCAzLjE5MTMyODgyLDE1LjY1NDE1NTcgQzMuMjM0NzI4NiwxNi41OTc2MzA1IDMuMzgzNzM0NTMsMTcuMjQxNTY2NCAzLjYwMjkwMzQ0LDE3LjgwNTE5MTMgQzMuODI1MzAxODMsMTguMzk2NzA0NSA0LjE3NDEwOTgsMTguOTMyNTI2MyA0LjYyNDk2ODM0LDE5LjM3NTIzNzQgQzUuMDY3MjY1MTcsMTkuODI2NDA4MyA1LjYwMjY5NzksMjAuMTc1NTU3NSA2LjE5Mzg3MDUyLDIwLjM5ODI5OTcgQzYuNzU3MzQ0MzgsMjAuNjE2ODAzOCA3LjQwMTEwNzg0LDIwLjc2NjU3MzIgOC4zNDQzMjk4LDIwLjgwOTk4NDYgQzkuMjg5NzIxNzYsMjAuODUyNjcyNSA5LjU5MTM1MDI1LDIwLjg2MzUyNTQgMTEuOTk3MTQ0OSwyMC44NjM1MjU0IEMxNC40MDM2NjI5LDIwLjg2MzUyNTQgMTQuNzA0NTY4MSwyMC44NTI2NzI1IDE1LjY0OTk2MDEsMjAuODA5OTg0NiBDMTYuNTkzMTgyLDIwLjc2NjU3MzIgMTcuMjM3NjY4OCwyMC42MTY4MDM4IDE3LjgwMTE0MjcsMjAuMzk4Mjk5NyBDMTguMzkyMTczMSwyMC4xNzU2OTQzIDE4LjkyNzM5NzYsMTkuODI2NTE5OSAxOS4zNjkzMjE1LDE5LjM3NTIzNzQgQzE5LjgyMDM4NjksMTguOTMyNTUzMiAyMC4xNjk0MzY5LDE4LjM5NjczODkgMjAuMzkyMTA5NywxNy44MDUxOTEzIEMyMC42MDk4MzIsMTcuMjQxNTY2NCAyMC43NTk1NjEzLDE2LjU5NzYzMDUgMjAuODAzNjg0NCwxNS42NTQxNTU3IEMyMC44NDYzNjA4LDE0LjcwOTIzNCAyMC44NTcyMTA4LDE0LjQwNzUyNDYgMjAuODU3MjEwOCwxMi4wMDEwODUzIEMyMC44NTcyMTA4LDkuNTkzOTIyNCAyMC44NDYzNjA4LDkuMjkyMjEzMDggMjAuODAzNjg0NCw4LjM0NjU2Nzc5IEMyMC43NTk1NjEzLDcuNDAzMDkzMDYgMjAuNjA5ODMyLDYuNzU5MTU3MSAyMC4zOTIxMDk3LDYuMTk1NTMyMjQgQzIwLjE2OTQyNzIsNS42MDQyMDEyIDE5LjgyMDM3MTUsNS4wNjg2MjQ5OSAxOS4zNjkzMjE1LDQuNjI2MjA5NjQgQzE4LjkyNzMxMDYsNC4xNzQ4NjM3NCAxOC4zOTE3ODA1LDMuODI1ODk3OTcgMTcuODAwNDE5MywzLjYwMzg3MDg1IEMxNy4yMzYyMjIxLDMuMzg0NjQzMjEgMTYuNTkxNzM1NCwzLjIzNTU5NzM2IDE1LjY0ODUxMzQsMy4xOTIxODU5NSBDMTQuNzAzODQ0OCwzLjE0OTQ5ODA2IDE0LjQwMjkzOTYsMy4xMzg2NDUyIDExLjk5NzE0NDksMy4xMzg2NDUyIFogTTExLjIwMzY1MjIsNC43MzU0NjE3IEwxMS45OTg1OTE2LDQuNzM1NDYxNyBDMTQuMzYzODc5OCw0LjczNTQ2MTcgMTQuNjQ0NTMxNyw0Ljc0NDE0Mzk4IDE1LjU3ODM1MDQsNC43ODY4MzE4NyBDMTYuNDQyNzI5NCw0LjgyNTkwMjE0IDE2LjkxMTQ0NzEsNC45NzA2MDY4NiAxNy4yMjMyMDIyLDUuMDkyMTU4ODEgQzE3LjYzNjk0NjgsNS4yNTI3ODEwNCAxNy45MzIwNjUzLDUuNDQ0NTE0NzkgMTguMjQxNjUwNSw1Ljc1NDkwNjM5IEMxOC41NTE5NTg5LDYuMDY0NTc0NDggMTguNzQzNjQxMyw2LjM2MDQ5NTYxIDE4LjkwNDk0MzgsNi43NzM2Mjc1NyBDMTkuMDI1NzM5OSw3LjA4NjE4OTc0IDE5LjE3MDQwNTgsNy41NTUwMzMwMSAxOS4yMDk0NjU2LDguNDE4OTIwMTQgQzE5LjI1MjE0MjEsOS4zNTI5ODkwNiAxOS4yNjE1NDU0LDkuNjMzNzE2MiAxOS4yNjE1NDU0LDExLjk5ODkxNDcgQzE5LjI2MTU0NTQsMTQuMzY0MTEzMiAxOS4yNTIxNDIxLDE0LjY0NDg0MDQgMTkuMjA5NDY1NiwxNS41Nzg5MDkzIEMxOS4xNzA0MDU4LDE2LjQ0Mjc5NjQgMTkuMDI1NzM5OSwxNi45MTE2Mzk3IDE4LjkwNDk0MzgsMTcuMjI0MjAxOSBDMTguNzYyNjcxMSwxNy42MDg4NDc5IDE4LjUzNjAxOCwxNy45NTY3MDY1IDE4LjI0MTY1MDUsMTguMjQyMTk5NSBDMTcuOTU2MDQ2NSwxOC41MzY1ODkxIDE3LjYwODAwMzEsMTguNzYzMDc1NyAxNy4yMjMyMDIyLDE4LjkwNDk0NzEgQzE2LjkxMTQ0NzEsMTkuMDI2NDk5MSAxNi40NDI3Mjk0LDE5LjE3MDQ4MDIgMTUuNTc4MzUwNCwxOS4yMTAyNzQgQzE0LjY0NDUzMTcsMTkuMjUyOTYxOSAxNC4zNjM4Nzk4LDE5LjI2MTY0NDIgMTEuOTk4NTkxNiwxOS4yNjE2NDQyIEM5LjYzMjU4MDA1LDE5LjI2MTY0NDIgOS4zNTE5MjgxMSwxOS4yNTI5NjE5IDguNDE4MTA5NDMsMTkuMjEwMjc0IEM3LjU1NDQ1Mzc0LDE5LjE3MDQ4MDIgNy4wODU3MzYwOCwxOS4wMjU3NzU1IDYuNzczMjU3NjMsMTguOTA0OTQ3MSBDNi4zODgzNzQ4LDE4Ljc2MjgxODQgNi4wNDAxNDkzMywxOC41MzYzNzQxIDUuNzU0MDg2MDUsMTguMjQyMTk5NSBDNS40NTk3NzUzLDE3Ljk1NjUxOSA1LjIzMzM0OTM4LDE3LjYwODM4MjQgNS4wOTE1MTYwMiwxNy4yMjM0NzgzIEM0Ljk2OTk5NjYyLDE2LjkxMDkxNjIgNC44MjUzMzA2OCwxNi40NDIwNzI5IDQuNzg2MjcwODcsMTUuNTc4MTg1OCBDNC43NDM1OTQ0MiwxNC42NDQxMTY4IDQuNzM0OTE0NDYsMTQuMzYzMzg5NyA0LjczNDkxNDQ2LDExLjk5Njc0NDEgQzQuNzM0OTE0NDYsOS42MzAwOTg1OCA0Ljc0MzU5NDQyLDkuMzUwODE4NDkgNC43ODYyNzA4Nyw4LjQxNjc0OTU3IEM0LjgyNTMzMDY4LDcuNTUyODYyNDQgNC45Njk5OTY2Miw3LjA4NDAxOTE3IDUuMDkxNTE2MDIsNi43NzE0NTcgQzUuMjMzMzE5ODIsNi4zODYzMjcxNCA1LjQ1OTc0MjM5LDYuMDM3OTQ4NDQgNS43NTQwODYwNSw1Ljc1MjAxMjMgQzYuMDQwMTQ5MzMsNS40NTc4Mzc3IDYuMzg4Mzc0OCw1LjIzMTM5MzQxIDYuNzczMjU3NjMsNS4wODkyNjQ3MiBDNy4wODU3MzYwOCw0Ljk2Njk4OTI0IDcuNTU0NDUzNzQsNC44MjMwMDgwNSA4LjQxODEwOTQzLDQuNzgzMjE0MjUgQzkuMjM1NDcyMDMsNC43NDYzMTQ1NSA5LjU1MjI5MDQ1LDQuNzM1NDYxNyAxMS4yMDM2NTIyLDQuNzMzMjkxMTMgTDExLjIwMzY1MjIsNC43MzU0NjE3IEwxMS4yMDM2NTIyLDQuNzM1NDYxNyBaIE0xMS45OTg1OTE2LDcuNDUwMTIyMDkgTDExLjk5Nzg2ODMsNy40NTAxMjIwOSBMMTEuOTk4NTkxNiw3LjQ1MDEyMjA5IEM5LjQ4NTgzNzM3LDcuNDUwMTIyMDkgNy40NDg4NDc2LDkuNDg3NjU3NzIgNy40NDg4NDc2LDEyLjAwMTA4NTMgQzcuNDQ4ODQ3NiwxNC41MTQ1MTI5IDkuNDg1ODM3MzcsMTYuNTUyMDQ4NSAxMS45OTg1OTE2LDE2LjU1MjA0ODUgQzE0LjUxMTM0NTgsMTYuNTUyMDQ4NSAxNi41NDgzMzU2LDE0LjUxNDUxMjkgMTYuNTQ4MzM1NiwxMi4wMDEwODUzIEMxNi41NDgzMzU2LDkuNDg3NjU3NzIgMTQuNTExMzQ1OCw3LjQ1MDEyMjA5IDExLjk5ODU5MTYsNy40NTAxMjIwOSBMMTEuOTk4NTkxNiw3LjQ1MDEyMjA5IFogTTExLjk5ODU5MTYsOS4wNDY5Mzg1OSBDMTMuMDUzNzIzOCw5LjA0NjkzODU3IDE0LjAyODcwNjIsOS42MDk5OTU3NCAxNC41NTYyNzIzLDEwLjUyNDAxMTkgQzE1LjA4MzgzODUsMTEuNDM4MDI4MSAxNS4wODM4Mzg1LDEyLjU2NDE0MjUgMTQuNTU2MjcyMywxMy40NzgxNTg3IEMxNC4wMjg3MDYyLDE0LjM5MjE3NDggMTMuMDUzNzIzOCwxNC45NTUyMzIgMTEuOTk4NTkxNiwxNC45NTUyMzIgQzEwLjM2NzQ5ODUsMTQuOTU1MjMxOSA5LjA0NTIzNjM3LDEzLjYzMjYxNTQgOS4wNDUyMzYzNywxMi4wMDEwODUzIEM5LjA0NTIzNjM3LDEwLjM2OTU1NTEgMTAuMzY3NDk4NSw5LjA0NjkzODYzIDExLjk5ODU5MTYsOS4wNDY5Mzg1OSBaIE0xNi43Mjc3MjEzLDYuMjA3MTA4NjIgQzE2LjM0Nzg0MjcsNi4yMDcxMDg2MSAxNS45OTY4MjA0LDYuNDA5ODI1NzQgMTUuODA2ODgxMSw2LjczODg5ODQzIEMxNS42MTY5NDE4LDcuMDY3OTcxMTEgMTUuNjE2OTQxOCw3LjQ3MzQwNTM5IDE1LjgwNjg4MTEsNy44MDI0NzgwOCBDMTUuOTk2ODIwNCw4LjEzMTU1MDc3IDE2LjM0Nzg0MjcsOC4zMzQyNjc5IDE2LjcyNzcyMTMsOC4zMzQyNjc4OCBDMTcuMzE0OTYyOCw4LjMzNDI2Nzg3IDE3Ljc5MTAxNiw3Ljg1ODA4NzA1IDE3Ljc5MTAxNiw3LjI3MDY4ODI1IEMxNy43OTEwMTYsNi42ODMyODk0NSAxNy4zMTQ5NjI4LDYuMjA3MTA4NjQgMTYuNzI3NzIxMyw2LjIwNzEwODYyIFoiIGlkPSJTaGFwZSIgZmlsbD0iI0NGQ0ZDRiIgZmlsbC1ydWxlPSJub256ZXJvIj48L3BhdGg+CiAgICAgICAgPC9nPgogICAgPC9nPgo8L3N2Zz4=);
}
.c0134 {
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjRweCIgaGVpZ2h0PSIyNHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUzLjEgKDcyNjMxKSAtIGh0dHBzOi8vc2tldGNoYXBwLmNvbSAtLT4KICAgIDx0aXRsZT5zb2NpYWwtZmFjZWJvb2staWNvbjwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxnIGlkPSJzb2NpYWwtZmFjZWJvb2staWNvbiIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9ImZhY2Vib29rIj4KICAgICAgICAgICAgPHBvbHlnb24gaWQ9IlBhdGgiIHBvaW50cz0iMCAwIDIzLjQyMTE3NjUgMCAyMy40MjExNzY1IDIzLjQyMTE3NjUgMCAyMy40MjExNzY1Ij48L3BvbHlnb24+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0yMi42NzU1ODc3LDAgQzIzLjQwNjg3MTYsMCAyNCwwLjU5MzEyODM5MSAyNCwxLjMyNDQxMjMgTDI0LDIyLjY3NTU4NzcgQzI0LDIzLjQwNjg3MTYgMjMuNDA2ODcxNiwyNCAyMi42NzU1ODc3LDI0IEwxNi41NTk4NTUzLDI0IEwxNi41NTk4NTUzLDE0LjcwNTk2NzUgTDE5LjY3ODg0MjcsMTQuNzA1OTY3NSBMMjAuMTQ2MTEyMSwxMS4wODM1NDQzIEwxNi41NTk4NTUzLDExLjA4MzU0NDMgTDE2LjU1OTg1NTMsOC43NzEwNjY5MSBDMTYuNTU5ODU1Myw3LjcyMjk2NTY0IDE2Ljg1MDYzMjksNy4wMDgzMTgyNiAxOC4zNTQ0MzA0LDcuMDA4MzE4MjYgTDIwLjI3MjY5NDQsNy4wMDY4NzE2MSBMMjAuMjcyNjk0NCwzLjc2NzgxMTkzIEMxOS45NDA2ODcyLDMuNzIzNjg4OTcgMTguODAyMTcsMy42MjQ1OTMxMyAxNy40Nzc3NTc3LDMuNjI0NTkzMTMgQzE0LjcxMjQ3NzQsMy42MjQ1OTMxMyAxMi44MTg4MDY1LDUuMzEyODM5MDYgMTIuODE4ODA2NSw4LjQxMjI5NjU2IEwxMi44MTg4MDY1LDExLjA4MzU0NDMgTDkuNjkxMTM5MjQsMTEuMDgzNTQ0MyBMOS42OTExMzkyNCwxNC43MDU5Njc1IEwxMi44MTg4MDY1LDE0LjcwNTk2NzUgTDEyLjgxODgwNjUsMjQgTDEuMzI0NDEyMywyNCBDMC41OTI5NTk1ODMsMjQgMi42ODgyMTkxOGUtMTUsMjMuNDA3MDQwNCAwLDIyLjY3NTU4NzcgTDAsMS4zMjQ0MTIzIEMwLDAuNTkzMTI4MzkxIDAuNTkzMTI4MzkxLDAgMS4zMjQ0MTIzLDAgTDIyLjY3NTU4NzcsMCBaIiBpZD0iUGF0aCIgZmlsbD0iI0NGQ0ZDRiIgZmlsbC1ydWxlPSJub256ZXJvIj48L3BhdGg+CiAgICAgICAgPC9nPgogICAgPC9nPgo8L3N2Zz4=);
}
.c0135 {
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjRweCIgaGVpZ2h0PSIyNHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUzLjEgKDcyNjMxKSAtIGh0dHBzOi8vc2tldGNoYXBwLmNvbSAtLT4KICAgIDx0aXRsZT5zb2NpYWwtdHdpdHRlci1pY29uPC90aXRsZT4KICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPgogICAgPGcgaWQ9InNvY2lhbC10d2l0dGVyLWljb24iIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxnIGlkPSJ0d2l0dGVyIj4KICAgICAgICAgICAgPHBvbHlnb24gaWQ9IlBhdGgiIHBvaW50cz0iMCAwIDIzLjQyMTE3NjUgMCAyMy40MjExNzY1IDIzLjQyMTE3NjUgMCAyMy40MjExNzY1Ij48L3BvbHlnb24+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik03LjU0NzkyMDQzLDIxLjQ2MzI3NDcgQzE2LjYwNDcwMTYsMjEuNDYzMjc0NyAyMS41NTczMjM3LDEzLjk1OTQ3NzIgMjEuNTU3MzIzNyw3LjQ1MzE0ODEzIEMyMS41NTczMjM3LDcuMjM5NzY2NTggMjEuNTUyOTgzNyw3LjAyNzgzMTY4IDIxLjU0MzU4MDUsNi44MTY2MjAxIEMyMi41MDU2MDU4LDYuMTIyMjI1ODkgMjMuMzQxMDQ4OCw1LjI1NDIzMzEyIDI0LDQuMjY2ODkxMzUgQzIzLjExNzU0MDcsNC42NTg5MzQ3NSAyMi4xNjc4MTE5LDQuOTIyOTQ5MjIgMjEuMTcxNzkwMiw1LjA0MjI5ODIyIEMyMi4yMDAwODE4LDQuNDI2ODk5NiAyMi45Njk3MTk0LDMuNDU4ODExNjUgMjMuMzM3NDMyMiwyLjMxODI0NzU5IEMyMi4zNzA3NzEsMi44OTE4Nzc2MiAyMS4zMTMyNjA1LDMuMjk2MjQxNDIgMjAuMjEwNDg4MiwzLjUxMzkwNzYzIEMxOC42ODY1NTA0LDEuODg0MTU0OTEgMTYuMjU3MjU1NCwxLjQ4MjcwMTA3IDE0LjI5MDAwODksMi41MzU1MTcxMiBDMTIuMzIyNzYyNCwzLjU4ODMzMzE4IDExLjMwOTIyMjIsNS44MzIyOTc0NiAxMS44MTk4OTE1LDguMDA0MzIzNTQgQzcuODYwMTY0NjMsNy44MDYwNjUxNiA0LjE3MDgyNDA4LDUuOTM1ODkxMzIgMS42NzAxNjI3NSwyLjg1OTI5NjQxIEMxLjIzMjYyNzQxLDMuNjEwNzcxNzggMS4wMDI0ODI4NSw0LjQ2NDk1MjU2IDEuMDAzMjU0OTcsNS4zMzQ1MjI0NSBDMS4wMDE4ODAxMiw2Ljk4MTM0OTYzIDEuODI0NTIwMDMsOC41MTk2NTkxIDMuMTk0OTM2NzEsOS40MzI4OTQ5NyBDMi40MTI4MDY1NCw5LjQwOTIxMjE0IDEuNjQ3NzkwNzIsOS4xOTgxMTM0MSAwLjk2NDE5NTI5OCw4LjgxNzM0MzQzIEwwLjk2NDE5NTI5OCw4Ljg4MDI3MjkxIEMwLjk2NDgzMTM4NCwxMS4yMjM4MjY5IDIuNjE2NTE4NTcsMTMuMjQyNDU0OSA0LjkxMzU2MjM5LDEzLjcwNzAzNiBDNC4xODg1Mjk4NCwxMy45MDQzNDE3IDMuNDI3OTg2ODQsMTMuOTMzMjg4OSAyLjY5MDA1NDI1LDEzLjc5MTY2NTMgQzMuMzM0Nzc4OTUsMTUuNzk3ODMwNSA1LjE4MzU2NTAyLDE3LjE3MjIxMTEgNy4yOTA0MTU5MSwxNy4yMTE1NTY4IEM1LjU0NjE3NDA1LDE4LjU4MDYxNzkgMy4zOTIwNDU1NiwxOS4zMjMwMzQzIDEuMTc0NjgzNTQsMTkuMzE5MzMyNiBDMC43NzY4NTM1MjYsMTkuMzE5MzMyNiAwLjM4NTUzMzQ1NCwxOS4yOTY5MDk0IDAsMTkuMjUxMzM5OCBDMi4yNTE0NjIxOCwyMC42OTc5NTA1IDQuODcxNzc0MzcsMjEuNDY1ODM4OCA3LjU0NzkyMDQzLDIxLjQ2MzI3NDciIGlkPSJQYXRoIiBmaWxsPSIjQ0ZDRkNGIiBmaWxsLXJ1bGU9Im5vbnplcm8iPjwvcGF0aD4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg==);
}
.c014 {
min-height: 800px;
overflow-x: auto;
background-size: cover;
background-position: center;
}
.c015 {
background: #333333;
}
.c015 + div {
display: none;
}
@media only screen and (max-width: 767px) {
.c014 {
background-position: 50% 100%;
}
}
.c016 {
background-image: url(/static/signin-hero-56bbcb135634620e65801089291bffb9.jpg);
background-color: ;
height: calc(100vh - 50px);
}
@media only screen and (max-width: 767px) {
.c016 {
background-image: url(/static/signin-hero-mobile-39de397b47237a03ec6c205ac218822b.jpg);
}
}
.c011 {
display: flex;
justify-content: center;
}
.c012 {
width: 350px;
display: flex;
border-radius: 0px;
justify-content: center;
background-color: #fafafa;
}
.c013 {
width: 100%;
height: 80%;
display: flex;
align-items: center;
justify-content: center;
}</style>This app works best with JavaScript enabled.

To sign in to your Garmin account, please enable JavaScript in your web browser.
<script id="gatsby-script-loader">//</script><script id="gatsby-chunk-mapping">//</script><script src="/webpack-runtime-30cc672d007f053cc67a.js" async=""></script><script src="/styles-7f4ad17eaa2f84285347.js" async=""></script><script src="/app-291b5bc865fa349ca640.js" async=""></script><script src="/0-83976d05cf027b253ce1.js" async=""></script><script src="/1-98fe992b4614ae528009.js" async=""></script><script src="/component---src-pages-signin-js-adeb213583a3e721e8d7.js" async=""></script>

}

Login randomly triggers password reset

It happened for multiple requests on multiple accounts. The problem is that when password reset is requested, Garmin sets up a temporary password and does not allow logging-in with the old one.

image

As mentioned, multiple requests with multiple accounts, and this does not happen always, but sometimes it does. I wonder if the requests frequency might have something to do with it? I send them every few minutes (5-10).

Deprecated request and cloudscraper notices

When running npm install or yarn:

warning cloudscraper > [email protected]: request-promise has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142
warning [email protected]: request has been deprecated, see https://github.com/request/request/issues/3142
warning request > [email protected]: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
warning request > [email protected]: this library is no longer supported
warning " > [email protected]" has unmet peer dependency "brotli@^1.3.2".

Then, when using the package in Next.js, the simplest usage such as calling this function on any api route:

import { GarminConnect } from "garmin-connect"

const URL =
  "https://connect.garmin.com/modern/proxy/badge-service/badge/detail/v2"

export default async (garminId: number) => {
  const credentials = { ... }

  const Garmin = new GarminConnect(credentials)
  await Garmin.login(credentials.username, credentials.password)
  return await Garmin.get(`${URL}/${garminId}`)
}

results in the following server logs for cloudscrapper dependency:

Critical dependency: require function is used in a way in which dependencies cannot be statically extracted

Import trace for requested module:
./node_modules/cloudscraper/lib/brotli.js
./node_modules/cloudscraper/index.js
./lib/garmin-connect/common/CFClient.ts
./lib/garmin-connect/garmin/GarminConnect.ts
./lib/garmin-connect/index.ts
./api/garmin/get-badge.ts
./app/api/jbadges/route.ts

and the following for app-root-path dependency:

Critical dependency: the request of a dependency is an expression

Import trace for requested module:
./node_modules/app-root-path/lib/app-root-path.js
./node_modules/app-root-path/index.js
./node_modules/garmin-connect/dist/garmin/GarminConnect.js
./node_modules/garmin-connect/dist/index.js
./api/garmin/get-badge.ts
./app/api/badges/route.ts

How to fix this?

IQ apps info

Hello, is it possible to receive Total Steps from IQ app activity? I'm using InlineSkate app but I don't know which request should I use for it :/

steps

custom request since v-1.6.0

since the update, I can't use GCClient.get(url)
I have this error message "Property 'get' does not exist on type 'GarminConnect'."

429: Access denied | sso.garmin.com used Cloudflare to restrict access

Has anyone else experienced this when calling GClient.login()?

Looks like i've been ratelimited by them, although I haven't made that many calls... hmm. I wonder how long this will last.

ERROR: (429), Too Many Requests, "<!DOCTYPE html>\n<!--[if lt IE 7]> <html class=\"no-js ie6 oldie\" lang=\"en-US\"> <![endif]-->\n<!--[if IE 7]>    <html class=\"no-js ie7 oldie\" lang=\"en-US\"> <![endif]-->\n<!--[if IE 8]>    <html class=\"no-js ie8 oldie\" lang=\"en-US\"> <![endif]-->\n<!--[if gt IE 8]><!--> <html class=\"no-js\" lang=\"en-US\"> <!--<![endif]-->\n<head>\n<title>Access denied | sso.garmin.com used Cloudflare to restrict access</title>\n<meta charset=\"UTF-8\" />\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n<meta http-equiv=\"X-UA-Compatible\" content=\"IE=Edge\" />\n<meta name=\"robots\" content=\"noindex, nofollow\" />\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n<link rel=\"stylesheet\" id=\"cf_styles-css\" href=\"/cdn-cgi/styles/main.css\" />\n\n\n<script>\n(function(){if(document.addEventListener&&window.XMLHttpRequest&&JSON&&JSON.stringify){var e=function(a){var c=document.getElementById(\"error-feedback-survey\"),d=document.getElementById(\"error-feedback-success\"),b=new XMLHttpRequest;a={event:\"feedback clicked\",properties:{errorCode:1015,helpful:a,version:1}};b.open(\"POST\",\"https://sparrow.cloudflare.com/api/v1/event\");

updateActivity

Hello, Im trying to change type of activity but it doesn't work.

const garmin_activities = await GCClient.getActivities(0, 1);
const garmin_activity = garmin_activities[0]
 var update = {
      typeId: 63,
      typeKey: 'inline_skating',
      parentTypeId: 4,
      isHidden: false,
      trimmable: true,
      restricted: false,
      sortOrder: null
}

garmin_activity['activityName'] = 'test'
garmin_activity['activityType'] = update
await GCClient.updateActivity(garmin_activity);

its only updating name, but activityType is staying same as before. Could you help please? :D Thanks!

HTTP 500 IllegalStateException on /group-service/keyword requests

Hi, I'm trying to add searching groups to the package. The endpoint is:

POST https://connect.garmin.com/modern/proxy/group-service/keyword

with the following payload:

{
  "keyword": "what-are-you-looking-for",
  "start": 1,
  "limit": 16
}

but when I try sending the request as follows:

import { GarminConnect } from "garmin-connect"

const credentials = {
  username: "",
  password: "",
}
const GCClient = new GarminConnect(credentials)
await GCClient.login("", "")

// NOTE: below is groups search
const keyword = "name"
const groupsSearchURL = "https://connect.garmin.com/modern/proxy/group-service/keyword"
const groups = await GCClient.post(groupsSearchURL, { keyword, start: 1, limit: 16 })

it returns HTTP 500 on the Garmin side:
image

The functionality does work in Garmin Connect when you go here: https://connect.garmin.com/modern/groups.

image
image
image

Any idea how to address this?

Promise Pending

Hi,
I am having promise pending while connection to login.

Can not sign in

Hello, can you check if everything is okay? Since 3 days I can not login, im getting html/css error with:

"To sign in to your Garmin account, please enable JavaScript in your web browser."

Error 429

Any way to resolve this problem?
Unhandled rejection StatusCodeError: 429 - "<!DOCTYPE html>\n<!--[if lt IE 7]> <html class=\"no-js ie6 oldie\" lang=\"en-US\"> <![endif]-->\n<!--[if IE 7]> <html class=\"no-js ie7 oldie\" lang=\"en-US\"> <![endif]-->\n<!--[if IE 8]> <html class=\"no-js ie8 oldie\" lang=\"en-US\"> <![endif]-->\n<!--[if gt IE 8]><!--> <html class=\"no-js\" lang=\"en-US\"> <!--<![endif]-->\n<head>\n<title>Access denied | sso.garmin.com used Cloudflare to restrict access</title>\n<meta charset=\"UTF-8\" />\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n<meta http-equiv=\"X-UA-Compatible\" content=\"IE=Edge,chrome=1\" />\n<meta name=\"robots\" content=\"noindex, nofollow\" />\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n<link rel=\"stylesheet\" id=\"cf_styles-css\" href=\"/cdn-cgi/styles/main.css\" type=\"text/css\" media=\"screen,projection\" />\n\n\n<script type=\"text/javascript\">\n(function(){if(document.addEventListener&&window.XMLHttpRequest&&JSON&&JSON.stringify){var e=function(a){var c=document.getElementById(\"error-feedback-survey\"),d=document.getElementById(\"error-feedback-success\"),b=new XMLHttpRequest;a={event:\"feedback clicked\",properties:{errorCode:1015,helpful:a,version:1}};b.open(\"POST\",\"https://sparrow.cloudflare.com/api/v1/event\");b.setRequestHeader(\"Content-Type\",\"application/json\");b.setRequestHeader(\"Sparrow-Source-Key\",\"c771f0e4b54944bebf4261d44bd79a1e\");\nb.send(JSON.stringify(a));c.classList.add(\"feedback-hidden\");d.classList.remove(\"feedback-hidden\")};document.addEventListener(\"DOMContentLoaded\",function(){var a=document.getElementById(\"error-feedback\"),c=document.getElementById(\"feedback-button-yes\"),d=document.getElementById(\"feedback-button-no\");\"classList\"in a&&(a.classList.remove(\"feedback-hidden\"),c.addEventListener(\"click\",function(){e(!0)}),d.addEventListener(\"click\",function(){e(!1)}))})}})();\n</script>\n\n<script defer src=\"https://api.radar.cloudflare.com/beacon.js\"></script>\n</head>\n<body>\n <div id=\"cf-wrapper\">\n <div class=\"cf-alert cf-alert-error cf-cookie-error hidden\" id=\"cookie-alert\" data-translate=\"enable_cookies\">Please enable cookies.</div>\n <div id=\"cf-error-details\" class=\"p-0\">\n <header class=\"mx-auto pt-10 lg:pt-6 lg:px-8 w-240 lg:w-full mb-15 antialiased\">\n <h1 class=\"inline-block md:block mr-2 md:mb-2 font-light text-60 md:text-3xl text-black-dark leading-tight\">\n <span data-translate=\"error\">Error</span>\n <span>1015</span>\n </h1>\n <span class=\"inline-block md:block heading-ray-id font-mono text-15 lg:text-sm lg:leading-relaxed\">Ray ID: 6e8dabfb5f7569de &bull;</span>\n <span class=\"inline-block md:block heading-ray-id font-mono text-15 lg:text-sm lg:leading-relaxed\">2022-03-08 18:35:38 UTC</span>\n <h2 class=\"text-gray-600 leading-1.3 text-3xl lg:text-2xl font-light\">You are being rate limited</h2>\n </header>\n\n <section class=\"w-240 lg:w-full mx-auto mb-8 lg:px-8\">\n <div id=\"what-happened-section\" class=\"w-1/2 md:w-full\">\n <h2 class=\"text-3xl leading-tight font-normal mb-4 text-black-dark antialiased\" data-translate=\"what_happened\">What happened?</h2>\n <p>The owner of this website (sso.garmin.com) has banned you temporarily from accessing this website.</p>\n \n </div>\n\n \n </section>\n\n \n <div class=\"feedback-hidden py-8 text-center\" id=\"error-feedback\">\n <div id=\"error-feedback-survey\">\n Was this page helpful?\n <button class=\"border border-solid bg-white cf-button cursor-pointer ml-4 px-4 py-2 rounded\" id=\"feedback-button-yes\" type=\"button\">Yes</button>\n <button class=\"border border-solid bg-white cf-button cursor-pointer ml-4 px-4 py-2 rounded\" id=\"feedback-button-no\" type=\"button\">No</button>\n </div>\n <div class=\"feedback-success feedback-hidden\" id=\"error-feedback-success\">\n Thank you for your feedback!\n </div>\n </div>\n \n\n <div class=\"cf-error-footer cf-wrapper w-240 lg:w-full py-10 sm:py-4 sm:px-8 mx-auto text-center sm:text-left border-solid border-0 border-t border-gray-300\">\n <p class=\"text-13\">\n <span class=\"cf-footer-item sm:block sm:mb-1\">Cloudflare Ray ID: <strong class=\"font-semibold\">6e8dabfb5f7569de</strong></span>\n <span class=\"cf-footer-separator sm:hidden\">&bull;</span>\n <span class=\"cf-footer-item sm:block sm:mb-1\"><span>Your IP</span>: 83.143.108.20</span>\n <span class=\"cf-footer-separator sm:hidden\">&bull;</span>\n <span class=\"cf-footer-item sm:block sm:mb-1\"><span>Performance &amp; security by</span> <a rel=\"noopener noreferrer\" href=\"https://www.cloudflare.com/5xx-error-landing\" id=\"brand_link\" target=\"_blank\">Cloudflare</a></span>\n \n </p>\n</div><!-- /.error-footer -->\n\n\n </div><!-- /#cf-error-details -->\n </div><!-- /#cf-wrapper -->\n\n <script type=\"text/javascript\">\n window._cf_translation = {};\n \n \n</script>\n\n</body>\n</html>\n" at new StatusCodeError ([/Users/davidgarcia/Desktop/garmin-nodejs/node_modules/request-promise-core/lib/errors.js:32:15]()) at Request.plumbing.callback ([/Users/davidgarcia/Desktop/garmin-nodejs/node_modules/request-promise-core/lib/plumbing.js:104:33]()) at Request.RP$callback [as _callback] ([/Users/davidgarcia/Desktop/garmin-nodejs/node_modules/request-promise-core/lib/plumbing.js:46:31]()) at self.callback ([/Users/davidgarcia/Desktop/garmin-nodejs/node_modules/request/request.js:185:22]()) at onRequestComplete ([/Users/davidgarcia/Desktop/garmin-nodejs/node_modules/cloudscraper/index.js:629:3]()) at onCloudflareResponse ([/Users/davidgarcia/Desktop/garmin-nodejs/node_modules/cloudscraper/index.js:251:3]()) at onRequestResponse ([/Users/davidgarcia/Desktop/garmin-nodejs/node_modules/cloudscraper/index.js:205:5]()) at Request.<anonymous> ([/Users/davidgarcia/Desktop/garmin-nodejs/node_modules/cloudscraper/index.js:149:7]()) at Object.onceWrapper (node:events:510:26) at Request.emit (node:events:390:28) at Request.<anonymous> ([/Users/davidgarcia/Desktop/garmin-nodejs/node_modules/request/request.js:1154:10]()) at Request.emit (node:events:390:28) at IncomingMessage.<anonymous> ([/Users/davidgarcia/Desktop/garmin-nodejs/node_modules/request/request.js:1076:12]()) at Object.onceWrapper (node:events:509:28) at IncomingMessage.emit (node:events:402:35) at endReadableNT (node:internal[/streams/readable:1343:12]()) at processTicksAndRejections (node:internal[/process/task_queues:83:21]()) node_modules/bluebird/js/release/debuggability.js:954

Thanks

ARM Login Issue

Hi,
Thanks a lot for your update of your library for the changed login.
On my Intel PC your solutions works great. But when I try it on the ARM platform on my Raspberry Pi, I get the following error when trying to login:

GarminConnect.js:105
throw new Error('Missing credentials');

Have you ever tried it on the ARM platform?
Thanks!
-Thomas

Is this maintainable?

Hey, I saw that the last update was in April and wanted to ask if you are still maintaining this project in case of bugs.

301 response

Hi! I've recently started to use this great library without any issues, but for some reason after a while i've start to get 301 when trying to access the user info GCClient.getUserInfo(). Do you know if this is a normal behavior? Maybe Garmin blocking me? Thanks a lot! :)

Edit: i see now a Too many request. Is there a limit, so, in the request we can made? Do you know of any limits?

Access to Garmin API Schema

@Pythe1337N - curious do you have a resource of the actual schema docs of the Garmin API? have a handful of improvements i'd like to make - currently just scraping the network tab right now

add sensor api to determine battery charge

on garmin connect web, if you click the watch icon at the top right, it displays various sensors connected to your garmin device(s) and their associated battery health. would be very useful for checking if your sensors (heart-rate monitor, power meter, wireless shifting, etc) need charging or new batteries.

api url: https://connect.garmin.com/device-service/sensors
response format: json

Error importing module on React

I received this error when running this line of code:
image

Here is the error:
image
image

Any suggestions on how to fix this? I've followed the "How to use" section on the readme, but it seems to not work. Not sure if this is a react-specific error.

Here is my full code:

import './App.css';

const { GarminConnect } = require('garmin-connect');

// Has to be run in an async function to be able to use the await keyword
const main = async () => {
    // Create a new Garmin Connect Client
   // const GCClient = new GarminConnect();

    // Uses credentials from garmin.config.json or uses supplied params
    // await GCClient.login('[email protected]', 'MySecretPassword');

    // // Get user info
    // const info = await GCClient.getUserInfo();

    // // Log info to make sure signin was successful
    // console.log(info);
};

// Run the code

function App() {
  main();

  return (
    <Watch></Watch>
  );
}

export default App;

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.