Use Garmin API from Elixir. Fetch activities, steps, and more!
Nimrag is Garmin in reverse, because we have to reverse engineer the API and auth. ¯_(ツ)_/¯
The package can be installed by adding Nimrag to your list of dependencies in mix.exs
:
def deps do
[
{:nimrag, "~> 0.1.0"}
]
end
If you'd like to use it from Livebook, take a look at this example or watch demo:
2024-04-08_18-53-28.mp4
Garmin doesn't have any official public API for individuals, only businesses. It means we're required to use username, password and (optionally) MFA code to obtain OAuth tokens. OAuth1 token is valid for up to a year and it's used to generate OAuth2 token that expires quickly, OAuth2 token is used for making the API calls. After OAuth1 token expires, we need to repeat the authentication process.
Please see Nimrag.Auth
docs for more details about authentication,
and see Nimrag.Credentials
on how to avoid providing plaintext credentials directly in code.
# If you're using it for the first time, we need to get OAuth Tokens first.
credentials = Nimrag.Credentials.new("username", "password")
# you may get prompted for MFA token on stdin
{:ok, client} = Nimrag.Auth.login_sso()
# OPTIONAL: If you'd like to store OAuth tokens in ~/.config/nimrag and not log in every time
:ok = Nimrag.Credentials.write_fs_oauth1_token(client)
:ok = Nimrag.Credentials.write_fs_oauth2_token(client)
Use functions from Nimrag
to fetch data from Garmin's API.
# Restore previously cached in ~/.nimrag OAuth tokens
client = Nimrag.Client.new() |> Nimrag.Client.with_auth(Nimrag.Credentials.read_oauth_tokens!())
# Fetch your profile
{:ok, %Nimrag.Api.Profile{} = profile, client} = Nimrag.profile(client)
# Fetch your latest activity
{:ok, %Nimrag.Api.Activity{} = activity, client} = Nimrag.last_activity(client)
# Call at the end of the session to cache new OAuth2 token
:ok = Nimrag.Credentials.write_fs_oauth2_token(client)
Nimrag
module has also functions with _req
suffix. They return {:ok, Req.Response{}, client}
and
do not process nor validate returned body. Other functions may return, if applicable,
structs with known fields.
This is very important split between response and transformation. Garmin's API may change at any time but it should still be possible to fallback to raw response if needed, so that any user of the library didn't have to wait for a fix.
API calls return {:ok, data, client}
or {:error, error}
. On success, client is there
so that it could be chained with always up to date OAuth2 token that will get
automatically updated when it's near expiration.
There's at this moment no extensive coverage of API endpoints, feel free to submit PR with new structs and endpoints, see Contributing.
By default, Nimrag uses Hammer for rate limiting requests.
If you are already using Hammer
, you can configure backend key via config.
config :nimrag, hammer: [backend: :custom]
By default, Hammer's ETS based backend will be started.
Nimrag is not using public Garmin's API so please be good citizens and don't hammer their servers.
See Nimrag.Client.new/1
for more details about changing the api limits.
Please do! Garmin has a lot of endpoints, some are useful, some are less useful and responses contain a lot of fields!
You can discover new endpoints by setting up mitmproxy and capturing traffic from mobile app or website. You can also take a look at python-garminconnect.
For local setup, the project has minimal dependencies and is easy to install
# fork and clone the repo
$ mix deps.get
# ensure everything works!
$ mix check
# do your changes
$ mix check
# submit PR!
# THANK YOU!
-
Add new function in
Nimrag
module, one with_req
suffix and one without. Functions with_req
should returns directNimrag.Api
result. -
Call
_req
function intest
env and save its response as fixture.Example for
Nimrag.profile/1
:$ MIX_ENV=test iex -S mix client = Nimrag.Client.new() |> Nimrag.Client.with_auth(Nimrag.Credentials.read_oauth_tokens!()) client |> Nimrag.profile_req() |> Nimrag.ApiHelper.store_response_as_test_fixture()
-
Add tests for new function in [
test/nimrag_test.exs
] -
Define new
Schematic
schema inNimrag.Api
, and ensure all tests pass.
Copyright © 2024 Michal Forys
This project is licensed under the MIT license.