mirror of
https://github.com/cyberjunky/home-assistant-garmin_connect.git
synced 2026-01-07 20:13:57 -05:00
Added HRV and weekly steps sensors
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
|
||||
# Garmin Connect
|
||||
|
||||
Integrate your Garmin Connect fitness data with Home Assistant. Access **100+ sensors** covering health metrics, activities, body composition, and gear tracking—plus add measurements directly to Garmin Connect via services.
|
||||
Integrate your Garmin Connect fitness data with Home Assistant. Access **110+ sensors** covering health metrics, activities, body composition, and gear tracking—plus add measurements directly to Garmin Connect via services.
|
||||
|
||||
## Supported Features
|
||||
|
||||
@@ -29,6 +29,8 @@ Sensor values depend on your Garmin devices and connected apps.
|
||||
|--------|-------------|
|
||||
| Total Steps | Daily step count |
|
||||
| Daily Step Goal | Your configured step target |
|
||||
| Yesterday Steps/Distance | Previous day's complete totals |
|
||||
| Weekly Step/Distance Avg | 7-day averages |
|
||||
| Total Distance | Distance walked/run in meters |
|
||||
| Floors Ascended/Descended | Floors climbed |
|
||||
|
||||
@@ -46,6 +48,8 @@ Sensor values depend on your Garmin devices and connected apps.
|
||||
| Resting Heart Rate | Daily resting HR |
|
||||
| Min/Max Heart Rate | Daily HR range |
|
||||
| Last 7 Days Avg HR | Weekly average |
|
||||
| HRV Weekly/Nightly Avg | Heart rate variability metrics |
|
||||
| HRV Baseline | Personal HRV baseline |
|
||||
|
||||
### Stress & Recovery
|
||||
|
||||
@@ -60,6 +64,9 @@ Sensor values depend on your Garmin devices and connected apps.
|
||||
|--------|-------------|
|
||||
| Sleep Score | Overall sleep quality score |
|
||||
| Sleep/Awake Duration | Time asleep and awake |
|
||||
| Deep Sleep | Time in deep sleep |
|
||||
| Light Sleep | Time in light sleep |
|
||||
| REM Sleep | Time in REM sleep |
|
||||
|
||||
### Body Battery
|
||||
|
||||
|
||||
@@ -96,9 +96,18 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
gear_defaults = {}
|
||||
activity_types = {}
|
||||
last_activities = []
|
||||
daily_steps: list[dict[str, Any]] = []
|
||||
yesterday_steps = None
|
||||
yesterday_distance = None
|
||||
weekly_step_avg = None
|
||||
weekly_distance_avg = None
|
||||
sleep_data = {}
|
||||
sleep_score = None
|
||||
sleep_time_seconds = None
|
||||
deep_sleep_seconds = None
|
||||
light_sleep_seconds = None
|
||||
rem_sleep_seconds = None
|
||||
awake_sleep_seconds = None
|
||||
hrv_data = {}
|
||||
hrv_status = {"status": "unknown"}
|
||||
endurance_data = {}
|
||||
@@ -113,6 +122,29 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
self.api.get_user_summary, today.isoformat()
|
||||
)
|
||||
|
||||
# Fetch last 7 days steps for weekly average and yesterday's final count
|
||||
week_ago = (today - timedelta(days=7)).isoformat()
|
||||
yesterday = (today - timedelta(days=1)).isoformat()
|
||||
daily_steps = await self.hass.async_add_executor_job(
|
||||
self.api.get_daily_steps, week_ago, yesterday
|
||||
)
|
||||
|
||||
# Process daily steps for yesterday values and weekly averages
|
||||
if daily_steps:
|
||||
# Yesterday is the last item in the list
|
||||
if daily_steps:
|
||||
yesterday_data = daily_steps[-1]
|
||||
yesterday_steps = yesterday_data.get("totalSteps")
|
||||
yesterday_distance = yesterday_data.get("totalDistance")
|
||||
|
||||
# Calculate weekly averages
|
||||
total_steps = sum(d.get("totalSteps", 0) for d in daily_steps)
|
||||
total_distance = sum(d.get("totalDistance", 0) for d in daily_steps)
|
||||
days_count = len(daily_steps)
|
||||
if days_count > 0:
|
||||
weekly_step_avg = round(total_steps / days_count)
|
||||
weekly_distance_avg = round(total_distance / days_count)
|
||||
|
||||
body = await self.hass.async_add_executor_job(
|
||||
self.api.get_body_composition, today.isoformat()
|
||||
)
|
||||
@@ -163,13 +195,11 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
menstrual_data = await self.hass.async_add_executor_job(
|
||||
self.api.get_menstrual_data_for_date, today.isoformat()
|
||||
)
|
||||
_LOGGER.debug("Menstrual data: %s", menstrual_data)
|
||||
# API returns None when not enabled - convert to empty dict
|
||||
if menstrual_data is None:
|
||||
menstrual_data = {}
|
||||
except Exception as err: # pylint: disable=broad-except
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# Menstrual data not available for this user
|
||||
_LOGGER.debug("Menstrual data error: %s", err)
|
||||
menstrual_data = {}
|
||||
|
||||
except (
|
||||
@@ -247,6 +277,26 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
deep_sleep_seconds = sleep_data["dailySleepDTO"]["deepSleepSeconds"]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
light_sleep_seconds = sleep_data["dailySleepDTO"]["lightSleepSeconds"]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
rem_sleep_seconds = sleep_data["dailySleepDTO"]["remSleepSeconds"]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
awake_sleep_seconds = sleep_data["dailySleepDTO"]["awakeSleepSeconds"]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
if hrv_data and "hrvSummary" in hrv_data:
|
||||
hrv_status = hrv_data["hrvSummary"]
|
||||
@@ -269,6 +319,14 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
"gearDefaults": gear_defaults,
|
||||
"sleepScore": sleep_score,
|
||||
"sleepTimeSeconds": sleep_time_seconds,
|
||||
"deepSleepSeconds": deep_sleep_seconds,
|
||||
"lightSleepSeconds": light_sleep_seconds,
|
||||
"remSleepSeconds": rem_sleep_seconds,
|
||||
"awakeSleepSeconds": awake_sleep_seconds,
|
||||
"yesterdaySteps": yesterday_steps,
|
||||
"yesterdayDistance": yesterday_distance,
|
||||
"weeklyStepAvg": weekly_step_avg,
|
||||
"weeklyDistanceAvg": weekly_distance_avg,
|
||||
"hrvStatus": hrv_status,
|
||||
"enduranceScore": endurance_status,
|
||||
**fitnessage_data,
|
||||
|
||||
@@ -49,6 +49,36 @@ ACTIVITY_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
||||
native_unit_of_measurement="steps",
|
||||
icon="mdi:target",
|
||||
),
|
||||
GarminConnectSensorEntityDescription(
|
||||
key="yesterdaySteps",
|
||||
translation_key="yesterday_steps",
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
native_unit_of_measurement="steps",
|
||||
icon="mdi:walk",
|
||||
),
|
||||
GarminConnectSensorEntityDescription(
|
||||
key="weeklyStepAvg",
|
||||
translation_key="weekly_step_avg",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
native_unit_of_measurement="steps",
|
||||
icon="mdi:chart-line",
|
||||
),
|
||||
GarminConnectSensorEntityDescription(
|
||||
key="yesterdayDistance",
|
||||
translation_key="yesterday_distance",
|
||||
device_class=SensorDeviceClass.DISTANCE,
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
native_unit_of_measurement=UnitOfLength.METERS,
|
||||
icon="mdi:map-marker-distance",
|
||||
),
|
||||
GarminConnectSensorEntityDescription(
|
||||
key="weeklyDistanceAvg",
|
||||
translation_key="weekly_distance_avg",
|
||||
device_class=SensorDeviceClass.DISTANCE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
native_unit_of_measurement=UnitOfLength.METERS,
|
||||
icon="mdi:chart-line",
|
||||
),
|
||||
GarminConnectSensorEntityDescription(
|
||||
key="totalDistanceMeters",
|
||||
translation_key="total_distance",
|
||||
@@ -168,6 +198,40 @@ HEART_RATE_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
||||
attributes_fn=lambda data: {k: v for k, v in data.get(
|
||||
"hrvStatus", {}).items() if k != "status"} if data.get("hrvStatus") else {},
|
||||
),
|
||||
GarminConnectSensorEntityDescription(
|
||||
key="hrvWeeklyAvg",
|
||||
translation_key="hrv_weekly_avg",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
native_unit_of_measurement="ms",
|
||||
icon="mdi:heart-pulse",
|
||||
value_fn=lambda data: data.get("hrvStatus", {}).get("weeklyAvg"),
|
||||
),
|
||||
GarminConnectSensorEntityDescription(
|
||||
key="hrvLastNightAvg",
|
||||
translation_key="hrv_last_night_avg",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
native_unit_of_measurement="ms",
|
||||
icon="mdi:heart-pulse",
|
||||
value_fn=lambda data: data.get("hrvStatus", {}).get("lastNightAvg"),
|
||||
),
|
||||
GarminConnectSensorEntityDescription(
|
||||
key="hrvLastNight5MinHigh",
|
||||
translation_key="hrv_last_night_5min_high",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
native_unit_of_measurement="ms",
|
||||
icon="mdi:heart-pulse",
|
||||
value_fn=lambda data: data.get("hrvStatus", {}).get("lastNight5MinHigh"),
|
||||
),
|
||||
GarminConnectSensorEntityDescription(
|
||||
key="hrvBaseline",
|
||||
translation_key="hrv_baseline",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
native_unit_of_measurement="ms",
|
||||
icon="mdi:heart-pulse",
|
||||
value_fn=lambda data: data.get("hrvStatus", {}).get("baseline", {}).get(
|
||||
"lowUpper") if data.get("hrvStatus", {}).get("baseline") else None,
|
||||
attributes_fn=lambda data: data.get("hrvStatus", {}).get("baseline", {}),
|
||||
),
|
||||
)
|
||||
|
||||
# Stress Sensors
|
||||
@@ -304,6 +368,46 @@ SLEEP_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
icon="mdi:sleep",
|
||||
),
|
||||
GarminConnectSensorEntityDescription(
|
||||
key="deepSleepSeconds",
|
||||
translation_key="deep_sleep",
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||
icon="mdi:sleep",
|
||||
value_fn=lambda data: round(data.get(
|
||||
"deepSleepSeconds", 0) / 60, 2) if data.get("deepSleepSeconds") else None,
|
||||
),
|
||||
GarminConnectSensorEntityDescription(
|
||||
key="lightSleepSeconds",
|
||||
translation_key="light_sleep",
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||
icon="mdi:sleep",
|
||||
value_fn=lambda data: round(data.get(
|
||||
"lightSleepSeconds", 0) / 60, 2) if data.get("lightSleepSeconds") else None,
|
||||
),
|
||||
GarminConnectSensorEntityDescription(
|
||||
key="remSleepSeconds",
|
||||
translation_key="rem_sleep",
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||
icon="mdi:sleep",
|
||||
value_fn=lambda data: round(data.get(
|
||||
"remSleepSeconds", 0) / 60, 2) if data.get("remSleepSeconds") else None,
|
||||
),
|
||||
GarminConnectSensorEntityDescription(
|
||||
key="awakeSleepSeconds",
|
||||
translation_key="awake_sleep",
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||
icon="mdi:sleep-off",
|
||||
value_fn=lambda data: round(data.get(
|
||||
"awakeSleepSeconds", 0) / 60, 2) if data.get("awakeSleepSeconds") else None,
|
||||
),
|
||||
)
|
||||
|
||||
# Body Battery Sensors
|
||||
|
||||
@@ -43,6 +43,18 @@
|
||||
"daily_step_goal": {
|
||||
"name": "Daily step goal"
|
||||
},
|
||||
"yesterday_steps": {
|
||||
"name": "Yesterday steps"
|
||||
},
|
||||
"weekly_step_avg": {
|
||||
"name": "Weekly step average"
|
||||
},
|
||||
"yesterday_distance": {
|
||||
"name": "Yesterday distance"
|
||||
},
|
||||
"weekly_distance_avg": {
|
||||
"name": "Weekly distance average"
|
||||
},
|
||||
"total_distance": {
|
||||
"name": "Total distance"
|
||||
},
|
||||
@@ -103,6 +115,18 @@
|
||||
"hrv_status": {
|
||||
"name": "HRV status"
|
||||
},
|
||||
"hrv_weekly_avg": {
|
||||
"name": "HRV weekly average"
|
||||
},
|
||||
"hrv_last_night_avg": {
|
||||
"name": "HRV last night average"
|
||||
},
|
||||
"hrv_last_night_5min_high": {
|
||||
"name": "HRV last night 5-min high"
|
||||
},
|
||||
"hrv_baseline": {
|
||||
"name": "HRV baseline"
|
||||
},
|
||||
"avg_stress_level": {
|
||||
"name": "Avg stress level"
|
||||
},
|
||||
@@ -172,6 +196,18 @@
|
||||
"sleep_score": {
|
||||
"name": "Sleep score"
|
||||
},
|
||||
"deep_sleep": {
|
||||
"name": "Deep sleep"
|
||||
},
|
||||
"light_sleep": {
|
||||
"name": "Light sleep"
|
||||
},
|
||||
"rem_sleep": {
|
||||
"name": "REM sleep"
|
||||
},
|
||||
"awake_sleep": {
|
||||
"name": "Awake during sleep"
|
||||
},
|
||||
"body_battery_most_recent": {
|
||||
"name": "Body battery (most recent)"
|
||||
},
|
||||
|
||||
@@ -25,12 +25,14 @@ You need a Garmin Connect account with at least one Garmin device that syncs dat
|
||||
|
||||
## Sensors
|
||||
|
||||
This integration provides **100+ sensors** covering various health and fitness metrics. Sensors are grouped into the following categories:
|
||||
This integration provides **110+ sensors** covering various health and fitness metrics. Sensors are grouped into the following categories:
|
||||
|
||||
### Activity & Steps
|
||||
|
||||
- **Total Steps** - Daily step count
|
||||
- **Daily Step Goal** - Your configured step target
|
||||
- **Yesterday Steps/Distance** - Previous day's complete totals
|
||||
- **Weekly Step/Distance Avg** - 7-day averages
|
||||
- **Total Distance** - Distance walked/run
|
||||
- **Floors Ascended/Descended** - Floors climbed
|
||||
|
||||
@@ -45,6 +47,9 @@ This integration provides **100+ sensors** covering various health and fitness m
|
||||
- **Resting Heart Rate** - Daily resting HR
|
||||
- **Min/Max Heart Rate** - Daily HR range
|
||||
- **Last 7 Days Avg HR** - Weekly average
|
||||
- **HRV Weekly Average** - 7-day HRV average (ms)
|
||||
- **HRV Last Night Average** - Last night's HRV (ms)
|
||||
- **HRV Baseline** - Personal HRV baseline with range
|
||||
|
||||
### Stress & Recovery
|
||||
|
||||
@@ -56,6 +61,9 @@ This integration provides **100+ sensors** covering various health and fitness m
|
||||
- **Sleep Score** - Overall sleep quality score
|
||||
- **Sleep Duration** - Time asleep
|
||||
- **Awake Duration** - Time awake during sleep
|
||||
- **Deep Sleep** - Time in deep sleep
|
||||
- **Light Sleep** - Time in light sleep
|
||||
- **REM Sleep** - Time in REM sleep
|
||||
|
||||
### Body Battery
|
||||
|
||||
|
||||
Reference in New Issue
Block a user