Add garmin data scraper on a timer
This commit is contained in:
parent
3b47a9b9e0
commit
45d7da3983
6 changed files with 119 additions and 0 deletions
|
@ -59,6 +59,7 @@
|
||||||
tetris.server = true;
|
tetris.server = true;
|
||||||
};
|
};
|
||||||
services = {
|
services = {
|
||||||
|
garmin-scraper.enable = true;
|
||||||
grafana.enable = true;
|
grafana.enable = true;
|
||||||
mail.enable = true;
|
mail.enable = true;
|
||||||
matrix.enable = true;
|
matrix.enable = true;
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
./containers
|
./containers
|
||||||
./data-access
|
./data-access
|
||||||
./deluge
|
./deluge
|
||||||
|
./garmin-scraper
|
||||||
./grafana
|
./grafana
|
||||||
./mail
|
./mail
|
||||||
./matrix
|
./matrix
|
||||||
|
|
37
modules/services/garmin-scraper/default.nix
Normal file
37
modules/services/garmin-scraper/default.nix
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
garmin2influx = pkgs.writers.writePython3Bin "garmin2influx" {
|
||||||
|
libraries = with pkgs.python3Packages; [ garminconnect influxdb-client ];
|
||||||
|
} (builtins.readFile ./garmin2influx.py);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.chvp.services.garmin-scraper.enable = lib.mkEnableOption "garmin scraper";
|
||||||
|
|
||||||
|
config = lib.mkIf config.chvp.services.garmin-scraper.enable {
|
||||||
|
# Install in environment to allow manual data collection
|
||||||
|
environment.systemPackages = [ garmin2influx ];
|
||||||
|
systemd = {
|
||||||
|
services.garmin2influx = {
|
||||||
|
description = "Garmin health data importer";
|
||||||
|
restartIfChanged = false;
|
||||||
|
unitConfig.X-StopOnRemoval = false;
|
||||||
|
serviceConfig = {
|
||||||
|
EnvironmentFile = config.age.secrets."passwords/services/garmin2influx-env".path;
|
||||||
|
Type = "oneshot";
|
||||||
|
User = "charlotte";
|
||||||
|
Group = "users";
|
||||||
|
ExecStart = "${garmin2influx}/bin/garmin2influx";
|
||||||
|
RestartSec = "5s";
|
||||||
|
Restart = "on-failure";
|
||||||
|
};
|
||||||
|
startAt = "02/4:00";
|
||||||
|
};
|
||||||
|
timers.garmin2influx.timerConfig.RandomizedDelaySec = "30min";
|
||||||
|
};
|
||||||
|
age.secrets."passwords/services/garmin2influx-env" = {
|
||||||
|
file = ../../../secrets/passwords/services/garmin2influx-env.age;
|
||||||
|
owner = "charlotte";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
66
modules/services/garmin-scraper/garmin2influx.py
Normal file
66
modules/services/garmin-scraper/garmin2influx.py
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from datetime import date, datetime, timedelta, timezone
|
||||||
|
from garminconnect import (
|
||||||
|
Garmin,
|
||||||
|
GarminConnectConnectionError,
|
||||||
|
GarminConnectTooManyRequestsError,
|
||||||
|
GarminConnectAuthenticationError,
|
||||||
|
)
|
||||||
|
from influxdb_client import InfluxDBClient, Point, WritePrecision
|
||||||
|
from influxdb_client.client.write_api import SYNCHRONOUS
|
||||||
|
|
||||||
|
email = os.getenv('EMAIL')
|
||||||
|
password = os.getenv('PASSWORD')
|
||||||
|
token = os.getenv('TOKEN')
|
||||||
|
org = 'default'
|
||||||
|
bucket = 'default'
|
||||||
|
|
||||||
|
|
||||||
|
def hr2point(time, val):
|
||||||
|
return Point("health") \
|
||||||
|
.field("heart_rate", val) \
|
||||||
|
.time(
|
||||||
|
datetime.fromtimestamp(time / 1000, timezone.utc),
|
||||||
|
WritePrecision.S
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def hr_for_date(api, date_to_fetch):
|
||||||
|
return api.get_heart_rates(date_to_fetch.isoformat())['heartRateValues']
|
||||||
|
|
||||||
|
|
||||||
|
date_to_fetch = date.today().isoformat()
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
date_to_fetch = sys.argv[1]
|
||||||
|
|
||||||
|
date_to_fetch = date.fromisoformat(date_to_fetch)
|
||||||
|
|
||||||
|
try:
|
||||||
|
api = Garmin(email, password)
|
||||||
|
api.login()
|
||||||
|
points = list(map(
|
||||||
|
lambda p: hr2point(*p),
|
||||||
|
hr_for_date(api, date_to_fetch - timedelta(days=1))
|
||||||
|
))
|
||||||
|
points += list(map(
|
||||||
|
lambda p: hr2point(*p),
|
||||||
|
hr_for_date(api, date_to_fetch)
|
||||||
|
))
|
||||||
|
with InfluxDBClient(
|
||||||
|
url="https://stats.chvp.be:8086",
|
||||||
|
token=token,
|
||||||
|
org=org
|
||||||
|
) as client:
|
||||||
|
write_api = client.write_api(write_options=SYNCHRONOUS)
|
||||||
|
write_api.write(bucket, org, points)
|
||||||
|
except (
|
||||||
|
GarminConnectConnectionError,
|
||||||
|
GarminConnectAuthenticationError,
|
||||||
|
GarminConnectTooManyRequestsError,
|
||||||
|
) as err:
|
||||||
|
print(
|
||||||
|
f'Error occured during Garmin Connect communication: {err}',
|
||||||
|
file=sys.stderr
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
|
@ -53,6 +53,7 @@ in
|
||||||
|
|
||||||
"secrets/passwords/services/acme.age".publicKeys = servers ++ users;
|
"secrets/passwords/services/acme.age".publicKeys = servers ++ users;
|
||||||
|
|
||||||
|
"secrets/passwords/services/garmin2influx-env.age".publicKeys = [ lasting-integrity ] ++ users;
|
||||||
"secrets/passwords/services/grafana/smtp.age".publicKeys = [ lasting-integrity ] ++ users;
|
"secrets/passwords/services/grafana/smtp.age".publicKeys = [ lasting-integrity ] ++ users;
|
||||||
"secrets/passwords/services/grafana/admin-password.age".publicKeys = [ lasting-integrity ] ++ users;
|
"secrets/passwords/services/grafana/admin-password.age".publicKeys = [ lasting-integrity ] ++ users;
|
||||||
"secrets/passwords/services/grafana/secret-key.age".publicKeys = [ lasting-integrity ] ++ users;
|
"secrets/passwords/services/grafana/secret-key.age".publicKeys = [ lasting-integrity ] ++ users;
|
||||||
|
|
13
secrets/passwords/services/garmin2influx-env.age
Normal file
13
secrets/passwords/services/garmin2influx-env.age
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
age-encryption.org/v1
|
||||||
|
-> ssh-ed25519 hKAFvQ Hx4N76JCCrv/Ybwgl2J0xrDJYcW4QUALyjQAGSfaeVw
|
||||||
|
IZijVgrusaqo0OlFI3aCmDe6dfmdVoKUWbqOYXEetsg
|
||||||
|
-> ssh-ed25519 s9rb8g dBblA0A3YJd0IkFgTTg/eQt6gUzQtZxRr6J2TU6NJwg
|
||||||
|
05rIEPNBVBIEK14JJ6LKLV8e9F7DmYuuKBg/ufXWYTs
|
||||||
|
-> ssh-ed25519 yad4VQ eZ4pWpGclpfgFsawKOEXa2AgPBkjOHZp9WmBVhfNXQA
|
||||||
|
FgSLcpVfIJsIgAAdH7rjJCFVBQ50qMHBXr6AMkhjSUw
|
||||||
|
-> ..-grease [IY!
|
||||||
|
3IuyW18ISiewloiloc1TZ+N7mfZaf+1TOrBJAnsNvvey0nzGSprmt8JCBrSBKKQF
|
||||||
|
yJ2Mcb2HMbe+
|
||||||
|
--- /xUIYvscrvdSBlSv9a5E+8Chv5VkhF1CDLuj5cPV/YY
|
||||||
|
JŸ`?“˜)‹0õЬù“öÆk|káneàK±³H1y¾öõt<C3B5>Y4°çhƒß<C692>æõ-V}ãfÚiÆšþEæä´cöÑgÄÁƒ]ù·ýFtqºC&@‚
|
||||||
|
‘òÅ¢Þ•„–§Lª²Ü‘7aKÁ^~ÉÔo!" Ôçy „¸.C
”pcoÅXæ<58>VºëH¸ô¹ôYO¨|þÅD½\îJCž™ó žš.¡£H“h›¶6•
|
Loading…
Add table
Add a link
Reference in a new issue