mirror of
https://github.com/cyberjunky/home-assistant-garmin_connect.git
synced 2026-01-08 20:38:00 -05:00
Added goal sensors, fixed polyline card
This commit is contained in:
@@ -58,9 +58,7 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
|
|||||||
"Token not found in config entry. Please reauthenticate."
|
"Token not found in config entry. Please reauthenticate."
|
||||||
)
|
)
|
||||||
|
|
||||||
await self.hass.async_add_executor_job(
|
await self.hass.async_add_executor_job(self.api.login, self.entry.data[CONF_TOKEN])
|
||||||
self.api.login, self.entry.data[CONF_TOKEN]
|
|
||||||
)
|
|
||||||
except ConfigEntryAuthFailed:
|
except ConfigEntryAuthFailed:
|
||||||
raise
|
raise
|
||||||
except GarminConnectAuthenticationError as err:
|
except GarminConnectAuthenticationError as err:
|
||||||
@@ -120,7 +118,12 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
|
|||||||
today = datetime.now(ZoneInfo(self.time_zone)).date()
|
today = datetime.now(ZoneInfo(self.time_zone)).date()
|
||||||
current_hour = datetime.now(ZoneInfo(self.time_zone)).hour
|
current_hour = datetime.now(ZoneInfo(self.time_zone)).hour
|
||||||
yesterday_date = (today - timedelta(days=1)).isoformat()
|
yesterday_date = (today - timedelta(days=1)).isoformat()
|
||||||
_LOGGER.debug("Fetching data for date: %s (timezone: %s, hour: %s)", today.isoformat(), self.time_zone, current_hour)
|
_LOGGER.debug(
|
||||||
|
"Fetching data for date: %s (timezone: %s, hour: %s)",
|
||||||
|
today.isoformat(),
|
||||||
|
self.time_zone,
|
||||||
|
current_hour,
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
summary = await self.hass.async_add_executor_job(
|
summary = await self.hass.async_add_executor_job(
|
||||||
@@ -130,15 +133,12 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
|
|||||||
# Smart fallback: detect when Garmin servers haven't populated today's data yet
|
# Smart fallback: detect when Garmin servers haven't populated today's data yet
|
||||||
# Key signal: dailyStepGoal is None means the day data structure doesn't exist
|
# Key signal: dailyStepGoal is None means the day data structure doesn't exist
|
||||||
# This works regardless of timezone - no fixed hour window needed
|
# This works regardless of timezone - no fixed hour window needed
|
||||||
today_data_not_ready = (
|
today_data_not_ready = not summary or summary.get("dailyStepGoal") is None
|
||||||
not summary
|
|
||||||
or summary.get("dailyStepGoal") is None
|
|
||||||
)
|
|
||||||
|
|
||||||
if today_data_not_ready:
|
if today_data_not_ready:
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"Today's data not ready (dailyStepGoal=%s), fetching yesterday's data",
|
"Today's data not ready (dailyStepGoal=%s), fetching yesterday's data",
|
||||||
summary.get("dailyStepGoal") if summary else None
|
summary.get("dailyStepGoal") if summary else None,
|
||||||
)
|
)
|
||||||
yesterday_summary = await self.hass.async_add_executor_job(
|
yesterday_summary = await self.hass.async_add_executor_job(
|
||||||
self.api.get_user_summary, yesterday_date
|
self.api.get_user_summary, yesterday_date
|
||||||
@@ -216,10 +216,10 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
|
|||||||
|
|
||||||
# Fetch workouts (scheduled/planned training sessions)
|
# Fetch workouts (scheduled/planned training sessions)
|
||||||
try:
|
try:
|
||||||
workouts = await self.hass.async_add_executor_job(
|
workouts = await self.hass.async_add_executor_job(self.api.get_workouts, 0, 10)
|
||||||
self.api.get_workouts, 0, 10
|
summary["workouts"] = (
|
||||||
|
workouts.get("workouts", []) if isinstance(workouts, dict) else workouts
|
||||||
)
|
)
|
||||||
summary["workouts"] = workouts.get("workouts", []) if isinstance(workouts, dict) else workouts
|
|
||||||
summary["lastWorkout"] = summary["workouts"][0] if summary["workouts"] else {}
|
summary["lastWorkout"] = summary["workouts"][0] if summary["workouts"] else {}
|
||||||
except Exception:
|
except Exception:
|
||||||
summary["workouts"] = []
|
summary["workouts"] = []
|
||||||
@@ -264,9 +264,27 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
|
|||||||
except Exception:
|
except Exception:
|
||||||
summary["lactateThreshold"] = {}
|
summary["lactateThreshold"] = {}
|
||||||
|
|
||||||
user_points = sum(
|
# Fetch goals (active, future, past)
|
||||||
badge["badgePoints"] * badge["badgeEarnedNumber"] for badge in badges
|
try:
|
||||||
)
|
active_goals = await self.hass.async_add_executor_job(self.api.get_goals, "active")
|
||||||
|
summary["activeGoals"] = active_goals or []
|
||||||
|
except Exception:
|
||||||
|
summary["activeGoals"] = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
future_goals = await self.hass.async_add_executor_job(self.api.get_goals, "future")
|
||||||
|
summary["futureGoals"] = future_goals or []
|
||||||
|
except Exception:
|
||||||
|
summary["futureGoals"] = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
past_goals = await self.hass.async_add_executor_job(self.api.get_goals, "past")
|
||||||
|
# Limit to last 10 completed goals
|
||||||
|
summary["goalsHistory"] = (past_goals or [])[:10]
|
||||||
|
except Exception:
|
||||||
|
summary["goalsHistory"] = []
|
||||||
|
|
||||||
|
user_points = sum(badge["badgePoints"] * badge["badgeEarnedNumber"] for badge in badges)
|
||||||
summary["userPoints"] = user_points
|
summary["userPoints"] = user_points
|
||||||
|
|
||||||
user_level = 0
|
user_level = 0
|
||||||
@@ -278,9 +296,7 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
|
|||||||
alarms = await self.hass.async_add_executor_job(self.api.get_device_alarms)
|
alarms = await self.hass.async_add_executor_job(self.api.get_device_alarms)
|
||||||
next_alarms = calculate_next_active_alarms(alarms, self.time_zone)
|
next_alarms = calculate_next_active_alarms(alarms, self.time_zone)
|
||||||
|
|
||||||
activity_types = await self.hass.async_add_executor_job(
|
activity_types = await self.hass.async_add_executor_job(self.api.get_activity_types)
|
||||||
self.api.get_activity_types
|
|
||||||
)
|
|
||||||
|
|
||||||
sleep_data = await self.hass.async_add_executor_job(
|
sleep_data = await self.hass.async_add_executor_job(
|
||||||
self.api.get_sleep_data, today.isoformat()
|
self.api.get_sleep_data, today.isoformat()
|
||||||
@@ -353,9 +369,7 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
|
|||||||
"bpSystolic": latest_bp.get("systolic"),
|
"bpSystolic": latest_bp.get("systolic"),
|
||||||
"bpDiastolic": latest_bp.get("diastolic"),
|
"bpDiastolic": latest_bp.get("diastolic"),
|
||||||
"bpPulse": latest_bp.get("pulse"),
|
"bpPulse": latest_bp.get("pulse"),
|
||||||
"bpMeasurementTime": latest_bp.get(
|
"bpMeasurementTime": latest_bp.get("measurementTimestampLocal"),
|
||||||
"measurementTimestampLocal"
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
_LOGGER.debug("Blood pressure data not available: %s", err)
|
_LOGGER.debug("Blood pressure data not available: %s", err)
|
||||||
@@ -380,9 +394,7 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
|
|||||||
try:
|
try:
|
||||||
if gear:
|
if gear:
|
||||||
tasks: list[Awaitable] = [
|
tasks: list[Awaitable] = [
|
||||||
self.hass.async_add_executor_job(
|
self.hass.async_add_executor_job(self.api.get_gear_stats, gear_item[Gear.UUID])
|
||||||
self.api.get_gear_stats, gear_item[Gear.UUID]
|
|
||||||
)
|
|
||||||
for gear_item in gear
|
for gear_item in gear
|
||||||
]
|
]
|
||||||
gear_stats = await asyncio.gather(*tasks)
|
gear_stats = await asyncio.gather(*tasks)
|
||||||
@@ -506,9 +518,7 @@ def calculate_next_active_alarms(alarms: Any, time_zone: str) -> list[str] | Non
|
|||||||
datetime.min.time(),
|
datetime.min.time(),
|
||||||
tzinfo=ZoneInfo(time_zone),
|
tzinfo=ZoneInfo(time_zone),
|
||||||
)
|
)
|
||||||
alarm = start_of_week + timedelta(
|
alarm = start_of_week + timedelta(days=DAY_TO_NUMBER[day] - 1, minutes=alarm_time)
|
||||||
days=DAY_TO_NUMBER[day] - 1, minutes=alarm_time
|
|
||||||
)
|
|
||||||
if alarm < now:
|
if alarm < now:
|
||||||
alarm += timedelta(days=7)
|
alarm += timedelta(days=7)
|
||||||
|
|
||||||
|
|||||||
@@ -82,7 +82,6 @@ class GarminConnectSensorEntityDescription(SensorEntityDescription):
|
|||||||
"""If True, preserve last known value when API returns None (for weight, BMI, etc)."""
|
"""If True, preserve last known value when API returns None (for weight, BMI, etc)."""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Activity & Steps Sensors
|
# Activity & Steps Sensors
|
||||||
ACTIVITY_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
ACTIVITY_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
@@ -196,7 +195,6 @@ CALORIES_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement="kcal",
|
native_unit_of_measurement="kcal",
|
||||||
icon="mdi:food",
|
icon="mdi:food",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="remainingKilocalories",
|
key="remainingKilocalories",
|
||||||
@@ -204,7 +202,6 @@ CALORIES_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement="kcal",
|
native_unit_of_measurement="kcal",
|
||||||
icon="mdi:food",
|
icon="mdi:food",
|
||||||
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -237,16 +234,19 @@ HEART_RATE_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
native_unit_of_measurement="bpm",
|
native_unit_of_measurement="bpm",
|
||||||
icon="mdi:heart-pulse",
|
icon="mdi:heart-pulse",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="hrvStatus",
|
key="hrvStatus",
|
||||||
translation_key="hrv_status",
|
translation_key="hrv_status",
|
||||||
icon="mdi:heart-pulse",
|
icon="mdi:heart-pulse",
|
||||||
value_fn=lambda data: data.get("hrvStatus", {}).get(
|
value_fn=lambda data: data.get("hrvStatus", {}).get("status", "").capitalize()
|
||||||
"status", "").capitalize() if data.get("hrvStatus") else None,
|
if data.get("hrvStatus")
|
||||||
attributes_fn=lambda data: {k: v for k, v in data.get(
|
else None,
|
||||||
"hrvStatus", {}).items() if k != "status"} if data.get("hrvStatus") else {},
|
attributes_fn=lambda data: {
|
||||||
|
k: v for k, v in data.get("hrvStatus", {}).items() if k != "status"
|
||||||
|
}
|
||||||
|
if data.get("hrvStatus")
|
||||||
|
else {},
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="hrvWeeklyAvg",
|
key="hrvWeeklyAvg",
|
||||||
@@ -278,8 +278,9 @@ HEART_RATE_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
native_unit_of_measurement="ms",
|
native_unit_of_measurement="ms",
|
||||||
icon="mdi:heart-pulse",
|
icon="mdi:heart-pulse",
|
||||||
value_fn=lambda data: data.get("hrvStatus", {}).get("baseline", {}).get(
|
value_fn=lambda data: data.get("hrvStatus", {}).get("baseline", {}).get("lowUpper")
|
||||||
"lowUpper") if data.get("hrvStatus", {}).get("baseline") else None,
|
if data.get("hrvStatus", {}).get("baseline")
|
||||||
|
else None,
|
||||||
attributes_fn=lambda data: data.get("hrvStatus", {}).get("baseline", {}),
|
attributes_fn=lambda data: data.get("hrvStatus", {}).get("baseline", {}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -304,9 +305,9 @@ STRESS_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
key="stressQualifier",
|
key="stressQualifier",
|
||||||
translation_key="stress_qualifier",
|
translation_key="stress_qualifier",
|
||||||
icon="mdi:emoticon",
|
icon="mdi:emoticon",
|
||||||
value_fn=lambda data: data.get("stressQualifier", "").capitalize(
|
value_fn=lambda data: data.get("stressQualifier", "").capitalize()
|
||||||
) if data.get("stressQualifier") else None,
|
if data.get("stressQualifier")
|
||||||
|
else None,
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="totalStressDuration",
|
key="totalStressDuration",
|
||||||
@@ -315,8 +316,9 @@ STRESS_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
icon="mdi:timer",
|
icon="mdi:timer",
|
||||||
value_fn=lambda data: round(data.get(
|
value_fn=lambda data: round(data.get("totalStressDuration", 0) / 60, 2)
|
||||||
"totalStressDuration", 0) / 60, 2) if data.get("totalStressDuration") else None,
|
if data.get("totalStressDuration")
|
||||||
|
else None,
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="restStressDuration",
|
key="restStressDuration",
|
||||||
@@ -325,8 +327,9 @@ STRESS_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
icon="mdi:timer-pause",
|
icon="mdi:timer-pause",
|
||||||
value_fn=lambda data: round(data.get(
|
value_fn=lambda data: round(data.get("restStressDuration", 0) / 60, 2)
|
||||||
"restStressDuration", 0) / 60, 2) if data.get("restStressDuration") else None,
|
if data.get("restStressDuration")
|
||||||
|
else None,
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="activityStressDuration",
|
key="activityStressDuration",
|
||||||
@@ -335,8 +338,9 @@ STRESS_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
icon="mdi:timer-play",
|
icon="mdi:timer-play",
|
||||||
value_fn=lambda data: round(data.get(
|
value_fn=lambda data: round(data.get("activityStressDuration", 0) / 60, 2)
|
||||||
"activityStressDuration", 0) / 60, 2) if data.get("activityStressDuration") else None,
|
if data.get("activityStressDuration")
|
||||||
|
else None,
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="lowStressDuration",
|
key="lowStressDuration",
|
||||||
@@ -345,8 +349,9 @@ STRESS_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
icon="mdi:timer-check",
|
icon="mdi:timer-check",
|
||||||
value_fn=lambda data: round(data.get(
|
value_fn=lambda data: round(data.get("lowStressDuration", 0) / 60, 2)
|
||||||
"lowStressDuration", 0) / 60, 2) if data.get("lowStressDuration") else None,
|
if data.get("lowStressDuration")
|
||||||
|
else None,
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="mediumStressDuration",
|
key="mediumStressDuration",
|
||||||
@@ -355,8 +360,9 @@ STRESS_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
icon="mdi:timer-alert",
|
icon="mdi:timer-alert",
|
||||||
value_fn=lambda data: round(data.get(
|
value_fn=lambda data: round(data.get("mediumStressDuration", 0) / 60, 2)
|
||||||
"mediumStressDuration", 0) / 60, 2) if data.get("mediumStressDuration") else None,
|
if data.get("mediumStressDuration")
|
||||||
|
else None,
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="highStressDuration",
|
key="highStressDuration",
|
||||||
@@ -365,8 +371,9 @@ STRESS_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
icon="mdi:timer-remove",
|
icon="mdi:timer-remove",
|
||||||
value_fn=lambda data: round(data.get(
|
value_fn=lambda data: round(data.get("highStressDuration", 0) / 60, 2)
|
||||||
"highStressDuration", 0) / 60, 2) if data.get("highStressDuration") else None,
|
if data.get("highStressDuration")
|
||||||
|
else None,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -379,8 +386,9 @@ SLEEP_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
icon="mdi:sleep",
|
icon="mdi:sleep",
|
||||||
value_fn=lambda data: round(data.get(
|
value_fn=lambda data: round(data.get("sleepingSeconds", 0) / 60, 2)
|
||||||
"sleepingSeconds", 0) / 60, 2) if data.get("sleepingSeconds") else None,
|
if data.get("sleepingSeconds")
|
||||||
|
else None,
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="sleepTimeSeconds",
|
key="sleepTimeSeconds",
|
||||||
@@ -389,8 +397,9 @@ SLEEP_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
icon="mdi:sleep",
|
icon="mdi:sleep",
|
||||||
value_fn=lambda data: round(data.get(
|
value_fn=lambda data: round(data.get("sleepTimeSeconds", 0) / 60, 2)
|
||||||
"sleepTimeSeconds", 0) / 60, 2) if data.get("sleepTimeSeconds") else None,
|
if data.get("sleepTimeSeconds")
|
||||||
|
else None,
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="measurableAsleepDuration",
|
key="measurableAsleepDuration",
|
||||||
@@ -399,8 +408,9 @@ SLEEP_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
icon="mdi:sleep",
|
icon="mdi:sleep",
|
||||||
value_fn=lambda data: round(data.get(
|
value_fn=lambda data: round(data.get("measurableAsleepDuration", 0) / 60, 2)
|
||||||
"measurableAsleepDuration", 0) / 60, 2) if data.get("measurableAsleepDuration") else None,
|
if data.get("measurableAsleepDuration")
|
||||||
|
else None,
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="measurableAwakeDuration",
|
key="measurableAwakeDuration",
|
||||||
@@ -409,8 +419,9 @@ SLEEP_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
icon="mdi:sleep-off",
|
icon="mdi:sleep-off",
|
||||||
value_fn=lambda data: round(data.get(
|
value_fn=lambda data: round(data.get("measurableAwakeDuration", 0) / 60, 2)
|
||||||
"measurableAwakeDuration", 0) / 60, 2) if data.get("measurableAwakeDuration") else None,
|
if data.get("measurableAwakeDuration")
|
||||||
|
else None,
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="sleepScore",
|
key="sleepScore",
|
||||||
@@ -425,8 +436,9 @@ SLEEP_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
icon="mdi:sleep",
|
icon="mdi:sleep",
|
||||||
value_fn=lambda data: round(data.get(
|
value_fn=lambda data: round(data.get("deepSleepSeconds", 0) / 60, 2)
|
||||||
"deepSleepSeconds", 0) / 60, 2) if data.get("deepSleepSeconds") else None,
|
if data.get("deepSleepSeconds")
|
||||||
|
else None,
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="lightSleepSeconds",
|
key="lightSleepSeconds",
|
||||||
@@ -435,8 +447,9 @@ SLEEP_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
icon="mdi:sleep",
|
icon="mdi:sleep",
|
||||||
value_fn=lambda data: round(data.get(
|
value_fn=lambda data: round(data.get("lightSleepSeconds", 0) / 60, 2)
|
||||||
"lightSleepSeconds", 0) / 60, 2) if data.get("lightSleepSeconds") else None,
|
if data.get("lightSleepSeconds")
|
||||||
|
else None,
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="remSleepSeconds",
|
key="remSleepSeconds",
|
||||||
@@ -445,8 +458,9 @@ SLEEP_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
icon="mdi:sleep",
|
icon="mdi:sleep",
|
||||||
value_fn=lambda data: round(data.get(
|
value_fn=lambda data: round(data.get("remSleepSeconds", 0) / 60, 2)
|
||||||
"remSleepSeconds", 0) / 60, 2) if data.get("remSleepSeconds") else None,
|
if data.get("remSleepSeconds")
|
||||||
|
else None,
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="awakeSleepSeconds",
|
key="awakeSleepSeconds",
|
||||||
@@ -455,8 +469,9 @@ SLEEP_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
icon="mdi:sleep-off",
|
icon="mdi:sleep-off",
|
||||||
value_fn=lambda data: round(data.get(
|
value_fn=lambda data: round(data.get("awakeSleepSeconds", 0) / 60, 2)
|
||||||
"awakeSleepSeconds", 0) / 60, 2) if data.get("awakeSleepSeconds") else None,
|
if data.get("awakeSleepSeconds")
|
||||||
|
else None,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -508,8 +523,9 @@ BODY_COMPOSITION_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
native_unit_of_measurement=UnitOfMass.KILOGRAMS,
|
native_unit_of_measurement=UnitOfMass.KILOGRAMS,
|
||||||
icon="mdi:weight-kilogram",
|
icon="mdi:weight-kilogram",
|
||||||
value_fn=lambda data: round(
|
value_fn=lambda data: round(data.get("weight", 0) / 1000, 2)
|
||||||
data.get("weight", 0) / 1000, 2) if data.get("weight") else None,
|
if data.get("weight")
|
||||||
|
else None,
|
||||||
preserve_value=True,
|
preserve_value=True,
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
@@ -543,8 +559,9 @@ BODY_COMPOSITION_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
native_unit_of_measurement=UnitOfMass.KILOGRAMS,
|
native_unit_of_measurement=UnitOfMass.KILOGRAMS,
|
||||||
icon="mdi:bone",
|
icon="mdi:bone",
|
||||||
value_fn=lambda data: round(
|
value_fn=lambda data: round(data.get("boneMass", 0) / 1000, 2)
|
||||||
data.get("boneMass", 0) / 1000, 2) if data.get("boneMass") else None,
|
if data.get("boneMass")
|
||||||
|
else None,
|
||||||
preserve_value=True,
|
preserve_value=True,
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
@@ -554,8 +571,9 @@ BODY_COMPOSITION_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
native_unit_of_measurement=UnitOfMass.KILOGRAMS,
|
native_unit_of_measurement=UnitOfMass.KILOGRAMS,
|
||||||
icon="mdi:dumbbell",
|
icon="mdi:dumbbell",
|
||||||
value_fn=lambda data: round(
|
value_fn=lambda data: round(data.get("muscleMass", 0) / 1000, 2)
|
||||||
data.get("muscleMass", 0) / 1000, 2) if data.get("muscleMass") else None,
|
if data.get("muscleMass")
|
||||||
|
else None,
|
||||||
preserve_value=True,
|
preserve_value=True,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -608,8 +626,9 @@ INTENSITY_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
icon="mdi:fire",
|
icon="mdi:fire",
|
||||||
value_fn=lambda data: round(
|
value_fn=lambda data: round(data.get("activeSeconds", 0) / 60, 2)
|
||||||
data.get("activeSeconds", 0) / 60, 2) if data.get("activeSeconds") else None,
|
if data.get("activeSeconds")
|
||||||
|
else None,
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="highlyActiveSeconds",
|
key="highlyActiveSeconds",
|
||||||
@@ -618,9 +637,9 @@ INTENSITY_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
icon="mdi:fire",
|
icon="mdi:fire",
|
||||||
value_fn=lambda data: round(data.get(
|
value_fn=lambda data: round(data.get("highlyActiveSeconds", 0) / 60, 2)
|
||||||
"highlyActiveSeconds", 0) / 60, 2) if data.get("highlyActiveSeconds") else None,
|
if data.get("highlyActiveSeconds")
|
||||||
|
else None,
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="sedentarySeconds",
|
key="sedentarySeconds",
|
||||||
@@ -629,8 +648,9 @@ INTENSITY_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
icon="mdi:seat",
|
icon="mdi:seat",
|
||||||
value_fn=lambda data: round(data.get(
|
value_fn=lambda data: round(data.get("sedentarySeconds", 0) / 60, 2)
|
||||||
"sedentarySeconds", 0) / 60, 2) if data.get("sedentarySeconds") else None,
|
if data.get("sedentarySeconds")
|
||||||
|
else None,
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="moderateIntensityMinutes",
|
key="moderateIntensityMinutes",
|
||||||
@@ -639,7 +659,6 @@ INTENSITY_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
icon="mdi:flash-alert",
|
icon="mdi:flash-alert",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="vigorousIntensityMinutes",
|
key="vigorousIntensityMinutes",
|
||||||
@@ -648,7 +667,6 @@ INTENSITY_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
icon="mdi:run-fast",
|
icon="mdi:run-fast",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="intensityMinutesGoal",
|
key="intensityMinutesGoal",
|
||||||
@@ -657,7 +675,6 @@ INTENSITY_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
icon="mdi:target",
|
icon="mdi:target",
|
||||||
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -689,7 +706,6 @@ HEALTH_MONITORING_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
translation_key="latest_spo2_time",
|
translation_key="latest_spo2_time",
|
||||||
device_class=SensorDeviceClass.TIMESTAMP,
|
device_class=SensorDeviceClass.TIMESTAMP,
|
||||||
icon="mdi:clock",
|
icon="mdi:clock",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="highestRespirationValue",
|
key="highestRespirationValue",
|
||||||
@@ -697,7 +713,6 @@ HEALTH_MONITORING_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
native_unit_of_measurement="brpm",
|
native_unit_of_measurement="brpm",
|
||||||
icon="mdi:progress-clock",
|
icon="mdi:progress-clock",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="lowestRespirationValue",
|
key="lowestRespirationValue",
|
||||||
@@ -705,7 +720,6 @@ HEALTH_MONITORING_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
native_unit_of_measurement="brpm",
|
native_unit_of_measurement="brpm",
|
||||||
icon="mdi:progress-clock",
|
icon="mdi:progress-clock",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="latestRespirationValue",
|
key="latestRespirationValue",
|
||||||
@@ -713,14 +727,12 @@ HEALTH_MONITORING_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
native_unit_of_measurement="brpm",
|
native_unit_of_measurement="brpm",
|
||||||
icon="mdi:progress-clock",
|
icon="mdi:progress-clock",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="latestRespirationTimeGMT",
|
key="latestRespirationTimeGMT",
|
||||||
translation_key="latest_respiration_time",
|
translation_key="latest_respiration_time",
|
||||||
device_class=SensorDeviceClass.TIMESTAMP,
|
device_class=SensorDeviceClass.TIMESTAMP,
|
||||||
icon="mdi:clock",
|
icon="mdi:clock",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="averageMonitoringEnvironmentAltitude",
|
key="averageMonitoringEnvironmentAltitude",
|
||||||
@@ -728,7 +740,6 @@ HEALTH_MONITORING_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=PERCENTAGE,
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
icon="mdi:image-filter-hdr",
|
icon="mdi:image-filter-hdr",
|
||||||
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -775,7 +786,6 @@ FITNESS_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
translation_key="endurance_score",
|
translation_key="endurance_score",
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
icon="mdi:run",
|
icon="mdi:run",
|
||||||
|
|
||||||
value_fn=lambda data: data.get("enduranceScore", {}).get("overallScore"),
|
value_fn=lambda data: data.get("enduranceScore", {}).get("overallScore"),
|
||||||
attributes_fn=lambda data: {
|
attributes_fn=lambda data: {
|
||||||
**{k: v for k, v in data.get("enduranceScore", {}).items() if k != "overallScore"},
|
**{k: v for k, v in data.get("enduranceScore", {}).items() if k != "overallScore"},
|
||||||
@@ -817,7 +827,6 @@ ACTIVITY_TRACKING_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
icon="mdi:alarm",
|
icon="mdi:alarm",
|
||||||
value_fn=lambda data: data.get("nextAlarm", [None])[0] if data.get("nextAlarm") else None,
|
value_fn=lambda data: data.get("nextAlarm", [None])[0] if data.get("nextAlarm") else None,
|
||||||
attributes_fn=lambda data: {
|
attributes_fn=lambda data: {
|
||||||
|
|
||||||
"next_alarms": data.get("nextAlarm"),
|
"next_alarms": data.get("nextAlarm"),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -825,7 +834,6 @@ ACTIVITY_TRACKING_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
key="lastActivity",
|
key="lastActivity",
|
||||||
translation_key="last_activity",
|
translation_key="last_activity",
|
||||||
icon="mdi:walk",
|
icon="mdi:walk",
|
||||||
|
|
||||||
value_fn=lambda data: data.get("lastActivity", {}).get("activityName"),
|
value_fn=lambda data: data.get("lastActivity", {}).get("activityName"),
|
||||||
attributes_fn=lambda data: _trim_activity(data.get("lastActivity", {})),
|
attributes_fn=lambda data: _trim_activity(data.get("lastActivity", {})),
|
||||||
),
|
),
|
||||||
@@ -834,11 +842,11 @@ ACTIVITY_TRACKING_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
translation_key="last_activities",
|
translation_key="last_activities",
|
||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
icon="mdi:numeric",
|
icon="mdi:numeric",
|
||||||
|
|
||||||
value_fn=lambda data: len(data.get("lastActivities", [])),
|
value_fn=lambda data: len(data.get("lastActivities", [])),
|
||||||
attributes_fn=lambda data: {
|
attributes_fn=lambda data: {
|
||||||
"last_activities": [
|
"last_activities": [
|
||||||
_trim_activity(a) for a in sorted(
|
_trim_activity(a)
|
||||||
|
for a in sorted(
|
||||||
data.get("lastActivities", []),
|
data.get("lastActivities", []),
|
||||||
key=lambda x: x.get("activityId", 0),
|
key=lambda x: x.get("activityId", 0),
|
||||||
)[-10:]
|
)[-10:]
|
||||||
@@ -849,7 +857,6 @@ ACTIVITY_TRACKING_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
key="lastWorkout",
|
key="lastWorkout",
|
||||||
translation_key="last_workout",
|
translation_key="last_workout",
|
||||||
icon="mdi:dumbbell",
|
icon="mdi:dumbbell",
|
||||||
|
|
||||||
value_fn=lambda data: data.get("lastWorkout", {}).get("workoutName"),
|
value_fn=lambda data: data.get("lastWorkout", {}).get("workoutName"),
|
||||||
attributes_fn=lambda data: data.get("lastWorkout", {}),
|
attributes_fn=lambda data: data.get("lastWorkout", {}),
|
||||||
),
|
),
|
||||||
@@ -858,7 +865,6 @@ ACTIVITY_TRACKING_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
translation_key="last_workouts",
|
translation_key="last_workouts",
|
||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
icon="mdi:dumbbell",
|
icon="mdi:dumbbell",
|
||||||
|
|
||||||
value_fn=lambda data: len(data.get("workouts", [])),
|
value_fn=lambda data: len(data.get("workouts", [])),
|
||||||
attributes_fn=lambda data: {
|
attributes_fn=lambda data: {
|
||||||
"last_workouts": data.get("workouts", [])[-10:],
|
"last_workouts": data.get("workouts", [])[-10:],
|
||||||
@@ -869,23 +875,25 @@ ACTIVITY_TRACKING_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
translation_key="training_readiness",
|
translation_key="training_readiness",
|
||||||
icon="mdi:run-fast",
|
icon="mdi:run-fast",
|
||||||
native_unit_of_measurement=PERCENTAGE,
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
|
|
||||||
value_fn=lambda data: data.get("trainingReadiness", {}).get("score")
|
value_fn=lambda data: data.get("trainingReadiness", {}).get("score")
|
||||||
if isinstance(data.get("trainingReadiness"), dict)
|
if isinstance(data.get("trainingReadiness"), dict)
|
||||||
else (data.get("trainingReadiness", [{}])[0].get("score")
|
else (
|
||||||
if isinstance(data.get("trainingReadiness"), list) and data.get("trainingReadiness")
|
data.get("trainingReadiness", [{}])[0].get("score")
|
||||||
else None),
|
if isinstance(data.get("trainingReadiness"), list) and data.get("trainingReadiness")
|
||||||
|
else None
|
||||||
|
),
|
||||||
attributes_fn=lambda data: data.get("trainingReadiness", {})
|
attributes_fn=lambda data: data.get("trainingReadiness", {})
|
||||||
if isinstance(data.get("trainingReadiness"), dict)
|
if isinstance(data.get("trainingReadiness"), dict)
|
||||||
else (data.get("trainingReadiness", [{}])[0]
|
else (
|
||||||
if isinstance(data.get("trainingReadiness"), list) and data.get("trainingReadiness")
|
data.get("trainingReadiness", [{}])[0]
|
||||||
else {}),
|
if isinstance(data.get("trainingReadiness"), list) and data.get("trainingReadiness")
|
||||||
|
else {}
|
||||||
|
),
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="trainingStatus",
|
key="trainingStatus",
|
||||||
translation_key="training_status",
|
translation_key="training_status",
|
||||||
icon="mdi:chart-line",
|
icon="mdi:chart-line",
|
||||||
|
|
||||||
value_fn=lambda data: data.get("trainingStatus", {}).get("trainingStatusPhrase"),
|
value_fn=lambda data: data.get("trainingStatus", {}).get("trainingStatusPhrase"),
|
||||||
attributes_fn=lambda data: data.get("trainingStatus", {}),
|
attributes_fn=lambda data: data.get("trainingStatus", {}),
|
||||||
),
|
),
|
||||||
@@ -894,7 +902,6 @@ ACTIVITY_TRACKING_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
translation_key="morning_training_readiness",
|
translation_key="morning_training_readiness",
|
||||||
icon="mdi:weather-sunset-up",
|
icon="mdi:weather-sunset-up",
|
||||||
native_unit_of_measurement=PERCENTAGE,
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
|
|
||||||
value_fn=lambda data: data.get("morningTrainingReadiness", {}).get("score"),
|
value_fn=lambda data: data.get("morningTrainingReadiness", {}).get("score"),
|
||||||
attributes_fn=lambda data: {
|
attributes_fn=lambda data: {
|
||||||
"level": data.get("morningTrainingReadiness", {}).get("level"),
|
"level": data.get("morningTrainingReadiness", {}).get("level"),
|
||||||
@@ -903,8 +910,20 @@ ACTIVITY_TRACKING_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
"hrv_status": data.get("morningTrainingReadiness", {}).get("hrvStatus"),
|
"hrv_status": data.get("morningTrainingReadiness", {}).get("hrvStatus"),
|
||||||
"acuteLoad": data.get("morningTrainingReadiness", {}).get("acuteLoad"),
|
"acuteLoad": data.get("morningTrainingReadiness", {}).get("acuteLoad"),
|
||||||
"input_context": data.get("morningTrainingReadiness", {}).get("inputContext"),
|
"input_context": data.get("morningTrainingReadiness", {}).get("inputContext"),
|
||||||
**{k: v for k, v in data.get("morningTrainingReadiness", {}).items()
|
**{
|
||||||
if k not in ("score", "level", "sleepScore", "recoveryScore", "hrvStatus", "acuteLoad", "inputContext")},
|
k: v
|
||||||
|
for k, v in data.get("morningTrainingReadiness", {}).items()
|
||||||
|
if k
|
||||||
|
not in (
|
||||||
|
"score",
|
||||||
|
"level",
|
||||||
|
"sleepScore",
|
||||||
|
"recoveryScore",
|
||||||
|
"hrvStatus",
|
||||||
|
"acuteLoad",
|
||||||
|
"inputContext",
|
||||||
|
)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
@@ -912,10 +931,9 @@ ACTIVITY_TRACKING_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
translation_key="lactate_threshold_hr",
|
translation_key="lactate_threshold_hr",
|
||||||
icon="mdi:heart-pulse",
|
icon="mdi:heart-pulse",
|
||||||
native_unit_of_measurement="bpm",
|
native_unit_of_measurement="bpm",
|
||||||
|
value_fn=lambda data: data.get("lactateThreshold", {})
|
||||||
value_fn=lambda data: data.get("lactateThreshold", {}).get(
|
.get("speed_and_heart_rate", {})
|
||||||
"speed_and_heart_rate", {}
|
.get("heartRate"),
|
||||||
).get("heartRate"),
|
|
||||||
attributes_fn=lambda data: data.get("lactateThreshold", {}),
|
attributes_fn=lambda data: data.get("lactateThreshold", {}),
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
@@ -923,10 +941,9 @@ ACTIVITY_TRACKING_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
translation_key="lactate_threshold_speed",
|
translation_key="lactate_threshold_speed",
|
||||||
icon="mdi:speedometer",
|
icon="mdi:speedometer",
|
||||||
native_unit_of_measurement="m/s",
|
native_unit_of_measurement="m/s",
|
||||||
|
value_fn=lambda data: data.get("lactateThreshold", {})
|
||||||
value_fn=lambda data: data.get("lactateThreshold", {}).get(
|
.get("speed_and_heart_rate", {})
|
||||||
"speed_and_heart_rate", {}
|
.get("speed"),
|
||||||
).get("speed"),
|
|
||||||
attributes_fn=lambda data: data.get("lactateThreshold", {}),
|
attributes_fn=lambda data: data.get("lactateThreshold", {}),
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
@@ -934,10 +951,8 @@ ACTIVITY_TRACKING_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
translation_key="badges",
|
translation_key="badges",
|
||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
icon="mdi:medal",
|
icon="mdi:medal",
|
||||||
|
|
||||||
value_fn=lambda data: len(data.get("badges", [])),
|
value_fn=lambda data: len(data.get("badges", [])),
|
||||||
attributes_fn=lambda data: {
|
attributes_fn=lambda data: {
|
||||||
|
|
||||||
"badges": sorted(
|
"badges": sorted(
|
||||||
data.get("badges", []),
|
data.get("badges", []),
|
||||||
key=lambda x: x.get("badgeEarnedDate", ""),
|
key=lambda x: x.get("badgeEarnedDate", ""),
|
||||||
@@ -949,14 +964,82 @@ ACTIVITY_TRACKING_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
translation_key="user_points",
|
translation_key="user_points",
|
||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
icon="mdi:counter",
|
icon="mdi:counter",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="userLevel",
|
key="userLevel",
|
||||||
translation_key="user_level",
|
translation_key="user_level",
|
||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
icon="mdi:star-four-points-circle",
|
icon="mdi:star-four-points-circle",
|
||||||
|
),
|
||||||
|
GarminConnectSensorEntityDescription(
|
||||||
|
key="activeGoals",
|
||||||
|
translation_key="active_goals",
|
||||||
|
state_class=SensorStateClass.TOTAL,
|
||||||
|
icon="mdi:flag-checkered",
|
||||||
|
value_fn=lambda data: len(data.get("activeGoals", [])),
|
||||||
|
attributes_fn=lambda data: {
|
||||||
|
"goals": [
|
||||||
|
{
|
||||||
|
"goalType": g.get("goalType"),
|
||||||
|
"goalDescription": g.get("goalDescription"),
|
||||||
|
"targetValue": g.get("targetValue"),
|
||||||
|
"currentValue": g.get("currentValue"),
|
||||||
|
"progressPercent": round(
|
||||||
|
(g.get("currentValue", 0) / g.get("targetValue", 1)) * 100, 1
|
||||||
|
)
|
||||||
|
if g.get("targetValue")
|
||||||
|
else 0,
|
||||||
|
"startDate": g.get("startDate"),
|
||||||
|
"endDate": g.get("endDate"),
|
||||||
|
"activityType": g.get("activityType"),
|
||||||
|
}
|
||||||
|
for g in data.get("activeGoals", [])
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GarminConnectSensorEntityDescription(
|
||||||
|
key="futureGoals",
|
||||||
|
translation_key="future_goals",
|
||||||
|
state_class=SensorStateClass.TOTAL,
|
||||||
|
icon="mdi:calendar-clock",
|
||||||
|
value_fn=lambda data: len(data.get("futureGoals", [])),
|
||||||
|
attributes_fn=lambda data: {
|
||||||
|
"goals": [
|
||||||
|
{
|
||||||
|
"goalType": g.get("goalType"),
|
||||||
|
"goalDescription": g.get("goalDescription"),
|
||||||
|
"targetValue": g.get("targetValue"),
|
||||||
|
"startDate": g.get("startDate"),
|
||||||
|
"endDate": g.get("endDate"),
|
||||||
|
"activityType": g.get("activityType"),
|
||||||
|
}
|
||||||
|
for g in data.get("futureGoals", [])
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GarminConnectSensorEntityDescription(
|
||||||
|
key="goalsHistory",
|
||||||
|
translation_key="goals_history",
|
||||||
|
state_class=SensorStateClass.TOTAL,
|
||||||
|
icon="mdi:history",
|
||||||
|
value_fn=lambda data: len(data.get("goalsHistory", [])),
|
||||||
|
attributes_fn=lambda data: {
|
||||||
|
"goals": [
|
||||||
|
{
|
||||||
|
"goalType": g.get("goalType"),
|
||||||
|
"goalDescription": g.get("goalDescription"),
|
||||||
|
"targetValue": g.get("targetValue"),
|
||||||
|
"currentValue": g.get("currentValue"),
|
||||||
|
"achieved": g.get("currentValue", 0) >= g.get("targetValue", 1)
|
||||||
|
if g.get("targetValue")
|
||||||
|
else False,
|
||||||
|
"startDate": g.get("startDate"),
|
||||||
|
"endDate": g.get("endDate"),
|
||||||
|
"activityType": g.get("activityType"),
|
||||||
|
}
|
||||||
|
for g in data.get("goalsHistory", [])
|
||||||
|
],
|
||||||
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -968,7 +1051,6 @@ ADDITIONAL_HEART_RATE_SENSORS: tuple[GarminConnectSensorEntityDescription, ...]
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
native_unit_of_measurement="bpm",
|
native_unit_of_measurement="bpm",
|
||||||
icon="mdi:heart-pulse",
|
icon="mdi:heart-pulse",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="maxAvgHeartRate",
|
key="maxAvgHeartRate",
|
||||||
@@ -976,14 +1058,12 @@ ADDITIONAL_HEART_RATE_SENSORS: tuple[GarminConnectSensorEntityDescription, ...]
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
native_unit_of_measurement="bpm",
|
native_unit_of_measurement="bpm",
|
||||||
icon="mdi:heart-pulse",
|
icon="mdi:heart-pulse",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="abnormalHeartRateAlertsCount",
|
key="abnormalHeartRateAlertsCount",
|
||||||
translation_key="abnormal_hr_alerts",
|
translation_key="abnormal_hr_alerts",
|
||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
icon="mdi:heart-pulse",
|
icon="mdi:heart-pulse",
|
||||||
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -995,7 +1075,6 @@ STRESS_PERCENTAGE_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
native_unit_of_measurement=PERCENTAGE,
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
icon="mdi:flash-alert",
|
icon="mdi:flash-alert",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="restStressPercentage",
|
key="restStressPercentage",
|
||||||
@@ -1003,7 +1082,6 @@ STRESS_PERCENTAGE_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
native_unit_of_measurement=PERCENTAGE,
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
icon="mdi:flash-alert",
|
icon="mdi:flash-alert",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="activityStressPercentage",
|
key="activityStressPercentage",
|
||||||
@@ -1011,7 +1089,6 @@ STRESS_PERCENTAGE_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
native_unit_of_measurement=PERCENTAGE,
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
icon="mdi:flash-alert",
|
icon="mdi:flash-alert",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="uncategorizedStressPercentage",
|
key="uncategorizedStressPercentage",
|
||||||
@@ -1019,7 +1096,6 @@ STRESS_PERCENTAGE_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
native_unit_of_measurement=PERCENTAGE,
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
icon="mdi:flash-alert",
|
icon="mdi:flash-alert",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="lowStressPercentage",
|
key="lowStressPercentage",
|
||||||
@@ -1027,7 +1103,6 @@ STRESS_PERCENTAGE_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
native_unit_of_measurement=PERCENTAGE,
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
icon="mdi:flash-alert",
|
icon="mdi:flash-alert",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="mediumStressPercentage",
|
key="mediumStressPercentage",
|
||||||
@@ -1035,7 +1110,6 @@ STRESS_PERCENTAGE_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
native_unit_of_measurement=PERCENTAGE,
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
icon="mdi:flash-alert",
|
icon="mdi:flash-alert",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="highStressPercentage",
|
key="highStressPercentage",
|
||||||
@@ -1043,7 +1117,6 @@ STRESS_PERCENTAGE_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
native_unit_of_measurement=PERCENTAGE,
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
icon="mdi:flash-alert",
|
icon="mdi:flash-alert",
|
||||||
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1056,8 +1129,9 @@ ADDITIONAL_STRESS_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
icon="mdi:flash-alert",
|
icon="mdi:flash-alert",
|
||||||
value_fn=lambda data: round(data.get(
|
value_fn=lambda data: round(data.get("uncategorizedStressDuration", 0) / 60, 2)
|
||||||
"uncategorizedStressDuration", 0) / 60, 2) if data.get("uncategorizedStressDuration") else None,
|
if data.get("uncategorizedStressDuration")
|
||||||
|
else None,
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="stressDuration",
|
key="stressDuration",
|
||||||
@@ -1066,9 +1140,9 @@ ADDITIONAL_STRESS_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
icon="mdi:flash-alert",
|
icon="mdi:flash-alert",
|
||||||
value_fn=lambda data: round(
|
value_fn=lambda data: round(data.get("stressDuration", 0) / 60, 2)
|
||||||
data.get("stressDuration", 0) / 60, 2) if data.get("stressDuration") else None,
|
if data.get("stressDuration")
|
||||||
|
else None,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1081,7 +1155,6 @@ ADDITIONAL_DISTANCE_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] =
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfLength.METERS,
|
native_unit_of_measurement=UnitOfLength.METERS,
|
||||||
icon="mdi:stairs-up",
|
icon="mdi:stairs-up",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="floorsDescendedInMeters",
|
key="floorsDescendedInMeters",
|
||||||
@@ -1090,7 +1163,6 @@ ADDITIONAL_DISTANCE_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] =
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfLength.METERS,
|
native_unit_of_measurement=UnitOfLength.METERS,
|
||||||
icon="mdi:stairs-down",
|
icon="mdi:stairs-down",
|
||||||
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1101,21 +1173,18 @@ WELLNESS_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
translation_key="wellness_start_time",
|
translation_key="wellness_start_time",
|
||||||
device_class=SensorDeviceClass.TIMESTAMP,
|
device_class=SensorDeviceClass.TIMESTAMP,
|
||||||
icon="mdi:clock",
|
icon="mdi:clock",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="wellnessEndTimeLocal",
|
key="wellnessEndTimeLocal",
|
||||||
translation_key="wellness_end_time",
|
translation_key="wellness_end_time",
|
||||||
device_class=SensorDeviceClass.TIMESTAMP,
|
device_class=SensorDeviceClass.TIMESTAMP,
|
||||||
icon="mdi:clock",
|
icon="mdi:clock",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="wellnessDescription",
|
key="wellnessDescription",
|
||||||
translation_key="wellness_description",
|
translation_key="wellness_description",
|
||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
icon="mdi:text",
|
icon="mdi:text",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="wellnessDistanceMeters",
|
key="wellnessDistanceMeters",
|
||||||
@@ -1124,7 +1193,6 @@ WELLNESS_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement=UnitOfLength.METERS,
|
native_unit_of_measurement=UnitOfLength.METERS,
|
||||||
icon="mdi:walk",
|
icon="mdi:walk",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="wellnessActiveKilocalories",
|
key="wellnessActiveKilocalories",
|
||||||
@@ -1132,7 +1200,6 @@ WELLNESS_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement="kcal",
|
native_unit_of_measurement="kcal",
|
||||||
icon="mdi:fire",
|
icon="mdi:fire",
|
||||||
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="wellnessKilocalories",
|
key="wellnessKilocalories",
|
||||||
@@ -1140,7 +1207,6 @@ WELLNESS_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
native_unit_of_measurement="kcal",
|
native_unit_of_measurement="kcal",
|
||||||
icon="mdi:fire",
|
icon="mdi:fire",
|
||||||
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1153,9 +1219,9 @@ MENSTRUAL_CYCLE_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
icon="mdi:calendar-heart",
|
icon="mdi:calendar-heart",
|
||||||
value_fn=lambda data: data.get("menstrualData", {}).get("currentPhase"),
|
value_fn=lambda data: data.get("menstrualData", {}).get("currentPhase"),
|
||||||
attributes_fn=lambda data: {
|
attributes_fn=lambda data: {
|
||||||
|
**{
|
||||||
**{k: v for k, v in data.get("menstrualData", {}).items()
|
k: v for k, v in data.get("menstrualData", {}).items() if k not in ("currentPhase",)
|
||||||
if k not in ("currentPhase",)},
|
},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
@@ -1164,9 +1230,7 @@ MENSTRUAL_CYCLE_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
icon="mdi:calendar-today",
|
icon="mdi:calendar-today",
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
value_fn=lambda data: data.get("menstrualData", {}).get("dayOfCycle"),
|
value_fn=lambda data: data.get("menstrualData", {}).get("dayOfCycle"),
|
||||||
attributes_fn=lambda data: {
|
attributes_fn=lambda data: {},
|
||||||
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="menstrualPeriodDay",
|
key="menstrualPeriodDay",
|
||||||
@@ -1174,9 +1238,7 @@ MENSTRUAL_CYCLE_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
icon="mdi:water",
|
icon="mdi:water",
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
value_fn=lambda data: data.get("menstrualData", {}).get("dayOfPeriod"),
|
value_fn=lambda data: data.get("menstrualData", {}).get("dayOfPeriod"),
|
||||||
attributes_fn=lambda data: {
|
attributes_fn=lambda data: {},
|
||||||
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="menstrualCycleLength",
|
key="menstrualCycleLength",
|
||||||
@@ -1185,9 +1247,7 @@ MENSTRUAL_CYCLE_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
native_unit_of_measurement=UnitOfTime.DAYS,
|
native_unit_of_measurement=UnitOfTime.DAYS,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
value_fn=lambda data: data.get("menstrualData", {}).get("cycleLength"),
|
value_fn=lambda data: data.get("menstrualData", {}).get("cycleLength"),
|
||||||
attributes_fn=lambda data: {
|
attributes_fn=lambda data: {},
|
||||||
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
GarminConnectSensorEntityDescription(
|
GarminConnectSensorEntityDescription(
|
||||||
key="menstrualPeriodLength",
|
key="menstrualPeriodLength",
|
||||||
@@ -1196,9 +1256,7 @@ MENSTRUAL_CYCLE_SENSORS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
native_unit_of_measurement=UnitOfTime.DAYS,
|
native_unit_of_measurement=UnitOfTime.DAYS,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
value_fn=lambda data: data.get("menstrualData", {}).get("periodLength"),
|
value_fn=lambda data: data.get("menstrualData", {}).get("periodLength"),
|
||||||
attributes_fn=lambda data: {
|
attributes_fn=lambda data: {},
|
||||||
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1273,6 +1331,3 @@ ALL_SENSOR_DESCRIPTIONS: tuple[GarminConnectSensorEntityDescription, ...] = (
|
|||||||
*BLOOD_PRESSURE_SENSORS,
|
*BLOOD_PRESSURE_SENSORS,
|
||||||
*DIAGNOSTIC_SENSORS,
|
*DIAGNOSTIC_SENSORS,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -367,6 +367,15 @@
|
|||||||
"user_level": {
|
"user_level": {
|
||||||
"name": "User level"
|
"name": "User level"
|
||||||
},
|
},
|
||||||
|
"active_goals": {
|
||||||
|
"name": "Active goals"
|
||||||
|
},
|
||||||
|
"future_goals": {
|
||||||
|
"name": "Future goals"
|
||||||
|
},
|
||||||
|
"goals_history": {
|
||||||
|
"name": "Goals history"
|
||||||
|
},
|
||||||
"wellness_start_time": {
|
"wellness_start_time": {
|
||||||
"name": "Wellness start time"
|
"name": "Wellness start time"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -105,6 +105,12 @@ class GarminPolylineCard extends HTMLElement {
|
|||||||
const mapContainer = this.shadowRoot.getElementById('map');
|
const mapContainer = this.shadowRoot.getElementById('map');
|
||||||
if (!mapContainer || !window.L) return;
|
if (!mapContainer || !window.L) return;
|
||||||
|
|
||||||
|
// If map already exists, remove it first
|
||||||
|
if (this._map) {
|
||||||
|
this._map.remove();
|
||||||
|
this._map = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Create map
|
// Create map
|
||||||
this._map = L.map(mapContainer, {
|
this._map = L.map(mapContainer, {
|
||||||
zoomControl: true,
|
zoomControl: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user