Skip to content

Commit 5fd88fc

Browse files
eskimokkveerendra2
andauthored
add -hrv parameters to fetch HRV data (#29)
* Add --hrv parameter to fetch HRV data * add -H/--hrv paramter to fetch HRV summary data (and keep clean) --------- Co-authored-by: Veerendra <functionkey5@protonmail.com>
1 parent 0bbfcad commit 5fd88fc

File tree

7 files changed

+98
-2
lines changed

7 files changed

+98
-2
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Access your Fitbit data directly from your terminal 💻. View 💤 sleep logs,
3131
| [Get AZM Time Series by Interval](https://dev.fitbit.com/build/reference/web-api/active-zone-minutes-timeseries/get-azm-timeseries-by-interval/) ||
3232
| [Get Breathing Rate Summary by Interval](https://dev.fitbit.com/build/reference/web-api/breathing-rate/get-br-summary-by-interval/) ||
3333
| [Get Daily Activity Summary](https://dev.fitbit.com/build/reference/web-api/activity/get-daily-activity-summary/) ||
34+
| [Get HRV Summary by Interval](https://dev.fitbit.com/build/reference/web-api/heartrate-variability/get-hrv-summary-by-interval/) ||
3435

3536
## Usage Guide
3637

@@ -45,7 +46,7 @@ python -m pip install fitbit-cli
4546
```bash
4647
fitbit-cli -h
4748
usage: fitbit-cli [-h] [-i] [-j] [-r] [-s [DATE[,DATE]|RELATIVE]] [-o [DATE[,DATE]|RELATIVE]] [-e [DATE[,DATE]|RELATIVE]] [-a [DATE[,DATE]|RELATIVE]]
48-
[-b [DATE[,DATE]|RELATIVE]] [-t [DATE[,DATE]|RELATIVE]] [-u] [-d] [-v]
49+
[-b [DATE[,DATE]|RELATIVE]] [-t [DATE[,DATE]|RELATIVE]] [-H [DATE[,DATE]|RELATIVE]] [-u] [-d] [-v]
4950

5051
Fitbit CLI -- Access your Fitbit data at your terminal.
5152

@@ -73,6 +74,8 @@ APIs:
7374
Show Breathing Rate Summary by Interval.
7475
-t, --activities [DATE[,DATE]|RELATIVE]
7576
Show Daily Activity Summary.
77+
-H, --hrv [DATE[,DATE]|RELATIVE]
78+
Show HRV Summary by Interval.
7679
-u, --user-profile Show Profile.
7780
-d, --devices Show Devices.
7881
```

fitbit_cli/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
fitbit_cli Module
44
"""
55

6-
__version__ = "1.6.0"
6+
__version__ = "1.7.0"

fitbit_cli/cli.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,15 @@ def parse_arguments():
137137
metavar="DATE[,DATE]|RELATIVE",
138138
help="Show Breathing Rate Summary by Interval.",
139139
)
140+
group.add_argument(
141+
"-H",
142+
"--hrv",
143+
type=parse_date_range,
144+
nargs="?",
145+
const=(datetime.today().date(), None),
146+
metavar="DATE[,DATE]|RELATIVE",
147+
help="Show HRV Summary by Interval.",
148+
)
140149
group.add_argument(
141150
"-t",
142151
"--activities",

fitbit_cli/fitbit_api.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,14 @@ def get_breathing_rate_intraday(self, start_date, end_date=None):
150150
response = self.make_request("GET", url)
151151
return response.json()
152152

153+
def get_hrv_summary(self, start_date, end_date=None):
154+
"""Get HRV Summary by Interval and Date"""
155+
156+
date_range = f"{start_date}/{end_date}" if end_date else start_date
157+
url = f"https://api.fitbit.com/1/user/-/hrv/date/{date_range}.json"
158+
response = self.make_request("GET", url)
159+
return response.json()
160+
153161
def get_daily_activity_summary(self, start_date):
154162
"""Get Daily Activity Summary"""
155163

fitbit_cli/formatter.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,38 @@ def display_breathing_rate(breathing_rate_data, as_json=False):
279279
return None
280280

281281

282+
def display_hrv(hrv_data, as_json=False):
283+
"""HRV data formatter"""
284+
285+
if as_json:
286+
return {
287+
"hrv": [
288+
{
289+
"date": h.get("dateTime"),
290+
"daily_rmssd": h.get("value", {}).get("dailyRmssd"),
291+
"deep_rmssd": h.get("value", {}).get("deepRmssd"),
292+
}
293+
for h in hrv_data.get("hrv", [])
294+
]
295+
}
296+
297+
table = Table(title="HRV Data Summary :heartpulse:", show_header=True)
298+
299+
table.add_column("Date :calendar:")
300+
table.add_column("Daily RMSSD :chart_with_upwards_trend:")
301+
table.add_column("Deep RMSSD :sleeping:")
302+
303+
for hrv in hrv_data.get("hrv", []):
304+
table.add_row(
305+
hrv.get("dateTime", "N/A"),
306+
str(hrv.get("value", {}).get("dailyRmssd", "N/A")),
307+
str(hrv.get("value", {}).get("deepRmssd", "N/A")),
308+
)
309+
310+
CONSOLE.print(table)
311+
return None
312+
313+
282314
def display_devices(devices, as_json=False):
283315
"""Devices list formatter"""
284316

fitbit_cli/output.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ def json_display(fitbit, args):
7272
fitbit.get_breathing_rate_summary(*args.breathing_rate), as_json=True
7373
)
7474
)
75+
if args.hrv:
76+
result.update(fmt.display_hrv(fitbit.get_hrv_summary(*args.hrv), as_json=True))
7577
if args.activities:
7678
activity_data = collect_activities(fitbit, args)
7779
if profile is None:
@@ -102,6 +104,8 @@ def raw_json_display(fitbit, args):
102104
result["breathing_rate"] = fitbit.get_breathing_rate_summary(
103105
*args.breathing_rate
104106
)
107+
if args.hrv:
108+
result["hrv"] = fitbit.get_hrv_summary(*args.hrv)
105109
if args.activities:
106110
result["activities"] = collect_activities(fitbit, args)
107111

@@ -129,6 +133,8 @@ def table_display(fitbit, args):
129133
fmt.display_breathing_rate(
130134
fitbit.get_breathing_rate_summary(*args.breathing_rate)
131135
)
136+
if args.hrv:
137+
fmt.display_hrv(fitbit.get_hrv_summary(*args.hrv))
132138
if args.activities:
133139
activity_data = collect_activities(fitbit, args)
134140
if profile is None:

tests/cli_test.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,44 @@ def test_init_auth_alone_parses_successfully(self):
185185
args = parse_arguments()
186186
self.assertTrue(args.init_auth)
187187

188+
@patch("sys.argv", ["fitbit-cli", "--hrv"])
189+
def test_hrv_flag_parses_successfully(self):
190+
"""Test that --hrv flag parses without error."""
191+
args = parse_arguments()
192+
self.assertIsNotNone(args.hrv)
193+
194+
@patch("sys.argv", ["fitbit-cli", "--json", "--hrv"])
195+
def test_hrv_with_json_flag_parses_successfully(self):
196+
"""Test that --hrv combined with --json parses without error."""
197+
args = parse_arguments()
198+
self.assertTrue(args.json)
199+
self.assertIsNotNone(args.hrv)
200+
201+
@patch("sys.argv", ["fitbit-cli", "--raw-json", "--hrv"])
202+
def test_hrv_with_raw_json_flag_parses_successfully(self):
203+
"""Test that --hrv combined with --raw-json parses without error."""
204+
args = parse_arguments()
205+
self.assertTrue(args.raw_json)
206+
self.assertIsNotNone(args.hrv)
207+
208+
@patch("sys.argv", ["fitbit-cli", "--hrv", "2024-01-01"])
209+
def test_hrv_with_single_date_parses_successfully(self):
210+
"""Test that --hrv with a single date parses without error."""
211+
args = parse_arguments()
212+
self.assertIsNotNone(args.hrv)
213+
214+
@patch("sys.argv", ["fitbit-cli", "--hrv", "2024-01-01,2024-01-07"])
215+
def test_hrv_with_date_range_parses_successfully(self):
216+
"""Test that --hrv with a date range parses without error."""
217+
args = parse_arguments()
218+
self.assertIsNotNone(args.hrv)
219+
220+
@patch("sys.argv", ["fitbit-cli", "--hrv", "last-week"])
221+
def test_hrv_with_relative_date_parses_successfully(self):
222+
"""Test that --hrv with a relative date parses without error."""
223+
args = parse_arguments()
224+
self.assertIsNotNone(args.hrv)
225+
188226

189227
if __name__ == "__main__":
190228
unittest.main()

0 commit comments

Comments
 (0)