diff --git a/clusters/homelab/apps/garmin/configmap.yaml b/clusters/homelab/apps/garmin/configmap.yaml index 56e3bc2..b7af6fd 100644 --- a/clusters/homelab/apps/garmin/configmap.yaml +++ b/clusters/homelab/apps/garmin/configmap.yaml @@ -63,73 +63,63 @@ data: ) """) - # Keys verified: (D) = aus deinem Debug-Output direkt bestaetigt - # (S) = aus Quellcode/Pydantic-Models cur.execute(""" CREATE TABLE IF NOT EXISTS daily_health ( date DATE PRIMARY KEY, - - -- get_stats (D) - resting_hr INTEGER, -- restingHeartRate - min_hr INTEGER, -- minHeartRate - max_hr INTEGER, -- maxHeartRate - total_steps INTEGER, -- totalSteps - daily_step_goal INTEGER, -- dailyStepGoal - total_distance_meters FLOAT, -- totalDistanceMeters - active_calories FLOAT, -- activeKilocalories - total_calories FLOAT, -- totalKilocalories - bmr_calories FLOAT, -- bmrKilocalories - highly_active_seconds INTEGER, -- highlyActiveSeconds - active_seconds INTEGER, -- activeSeconds - sedentary_seconds INTEGER, -- sedentarySeconds - sleeping_seconds INTEGER, -- sleepingSeconds - moderate_intensity_minutes INTEGER, -- moderateIntensityMinutes - vigorous_intensity_minutes INTEGER, -- vigorousIntensityMinutes - floors_ascended FLOAT, -- floorsAscended - floors_descended FLOAT, -- floorsDescended - average_stress INTEGER, -- averageStressLevel - max_stress INTEGER, -- maxStressLevel - rest_stress_duration INTEGER, -- restStressDuration - low_stress_duration INTEGER, -- lowStressDuration - medium_stress_duration INTEGER, -- mediumStressDuration - high_stress_duration INTEGER, -- highStressDuration - stress_qualifier TEXT, -- stressQualifier - body_battery_highest INTEGER, -- bodyBatteryHighestValue - body_battery_lowest INTEGER, -- bodyBatteryLowestValue - body_battery_most_recent INTEGER, -- bodyBatteryMostRecentValue - body_battery_at_wake INTEGER, -- bodyBatteryAtWakeTime - body_battery_during_sleep INTEGER, -- bodyBatteryDuringSleep - average_spo2 FLOAT, -- averageSpo2 - lowest_spo2 FLOAT, -- lowestSpo2 - latest_spo2 FLOAT, -- latestSpo2 - - -- get_hrv_data -> hrvSummary (S - wird null wenn Uhr nicht getragen) - hrv_weekly_avg FLOAT, -- weeklyAvg - hrv_last_night FLOAT, -- lastNight - hrv_last_night_5min_high FLOAT, -- lastNight5MinHigh - hrv_baseline_low FLOAT, -- baselineLowUpper - hrv_baseline_high FLOAT, -- baselineBalancedLower - hrv_status TEXT, -- status - hrv_feedback TEXT, -- feedbackPhrase - - -- get_sleep_data -> dailySleepDTO (D) - sleep_start_local TIMESTAMP, -- sleepStartTimestampLocal - sleep_end_local TIMESTAMP, -- sleepEndTimestampLocal - sleep_duration_seconds INTEGER, -- sleepTimeSeconds - sleep_nap_seconds INTEGER, -- napTimeSeconds - sleep_deep_seconds INTEGER, -- deepSleepSeconds - sleep_light_seconds INTEGER, -- lightSleepSeconds - sleep_rem_seconds INTEGER, -- remSleepSeconds - sleep_awake_seconds INTEGER, -- awakeSleepSeconds - sleep_rem_capable BOOLEAN, -- deviceRemCapable - sleep_score INTEGER, -- sleepScores.overall.value - sleep_feedback TEXT, -- sleepScores.overall.qualifierKey - - -- get_respiration_data (D) - respiration_avg_waking FLOAT, -- avgWakingRespirationValue - respiration_avg_sleep FLOAT, -- avgSleepRespirationValue - respiration_lowest FLOAT, -- lowestRespirationValue - respiration_highest FLOAT -- highestRespirationValue + resting_hr INTEGER, + min_hr INTEGER, + max_hr INTEGER, + total_steps INTEGER, + daily_step_goal INTEGER, + total_distance_meters FLOAT, + active_calories FLOAT, + total_calories FLOAT, + bmr_calories FLOAT, + highly_active_seconds INTEGER, + active_seconds INTEGER, + sedentary_seconds INTEGER, + sleeping_seconds INTEGER, + moderate_intensity_minutes INTEGER, + vigorous_intensity_minutes INTEGER, + floors_ascended FLOAT, + floors_descended FLOAT, + average_stress INTEGER, + max_stress INTEGER, + rest_stress_duration INTEGER, + low_stress_duration INTEGER, + medium_stress_duration INTEGER, + high_stress_duration INTEGER, + stress_qualifier TEXT, + body_battery_highest INTEGER, + body_battery_lowest INTEGER, + body_battery_most_recent INTEGER, + body_battery_at_wake INTEGER, + body_battery_during_sleep INTEGER, + average_spo2 FLOAT, + lowest_spo2 FLOAT, + latest_spo2 FLOAT, + hrv_weekly_avg FLOAT, + hrv_last_night FLOAT, + hrv_last_night_5min_high FLOAT, + hrv_baseline_low FLOAT, + hrv_baseline_high FLOAT, + hrv_status TEXT, + hrv_feedback TEXT, + sleep_start_local TIMESTAMP, + sleep_end_local TIMESTAMP, + sleep_duration_seconds INTEGER, + sleep_nap_seconds INTEGER, + sleep_deep_seconds INTEGER, + sleep_light_seconds INTEGER, + sleep_rem_seconds INTEGER, + sleep_awake_seconds INTEGER, + sleep_rem_capable BOOLEAN, + sleep_score INTEGER, + sleep_feedback TEXT, + respiration_avg_waking FLOAT, + respiration_avg_sleep FLOAT, + respiration_lowest FLOAT, + respiration_highest FLOAT ) """) @@ -198,6 +188,50 @@ data: print(f" Warnung {fn.__name__}: {e}") return None + COLS = """ + date, + resting_hr, min_hr, max_hr, + total_steps, daily_step_goal, total_distance_meters, + active_calories, total_calories, bmr_calories, + highly_active_seconds, active_seconds, + sedentary_seconds, sleeping_seconds, + moderate_intensity_minutes, vigorous_intensity_minutes, + floors_ascended, floors_descended, + average_stress, max_stress, + rest_stress_duration, low_stress_duration, + medium_stress_duration, high_stress_duration, + stress_qualifier, + body_battery_highest, body_battery_lowest, + body_battery_most_recent, body_battery_at_wake, + body_battery_during_sleep, + average_spo2, lowest_spo2, latest_spo2, + hrv_weekly_avg, hrv_last_night, hrv_last_night_5min_high, + hrv_baseline_low, hrv_baseline_high, + hrv_status, hrv_feedback, + sleep_start_local, sleep_end_local, + sleep_duration_seconds, sleep_nap_seconds, + sleep_deep_seconds, sleep_light_seconds, + sleep_rem_seconds, sleep_awake_seconds, + sleep_rem_capable, sleep_score, sleep_feedback, + respiration_avg_waking, respiration_avg_sleep, + respiration_lowest, respiration_highest + """ + + N = len([c for c in COLS.split(",") if c.strip()]) # = 55 + PLACEHOLDERS = ",".join(["%s"] * N) + + UPDATE = ", ".join( + f"{c.strip()} = EXCLUDED.{c.strip()}" + for c in COLS.split(",") + if c.strip() and c.strip() != "date" + ) + + INSERT_SQL = f""" + INSERT INTO daily_health ({COLS}) + VALUES ({PLACEHOLDERS}) + ON CONFLICT (date) DO UPDATE SET {UPDATE} + """ + synced = 0 check_date = today - timedelta(days=7) @@ -211,123 +245,10 @@ data: hrv = hrv_raw.get("hrvSummary") or {} sleep = sleep_r.get("dailySleepDTO") or {} - scores = ((sleep.get("sleepScores") or {}).get("overall") or {}) + scores = (sleep.get("sleepScores") or {}).get("overall") or {} - cur.execute(""" - INSERT INTO daily_health ( - date, - resting_hr, min_hr, max_hr, - total_steps, daily_step_goal, total_distance_meters, - active_calories, total_calories, bmr_calories, - highly_active_seconds, active_seconds, - sedentary_seconds, sleeping_seconds, - moderate_intensity_minutes, vigorous_intensity_minutes, - floors_ascended, floors_descended, - average_stress, max_stress, - rest_stress_duration, low_stress_duration, - medium_stress_duration, high_stress_duration, - stress_qualifier, - body_battery_highest, body_battery_lowest, - body_battery_most_recent, body_battery_at_wake, - body_battery_during_sleep, - average_spo2, lowest_spo2, latest_spo2, - hrv_weekly_avg, hrv_last_night, hrv_last_night_5min_high, - hrv_baseline_low, hrv_baseline_high, - hrv_status, hrv_feedback, - sleep_start_local, sleep_end_local, - sleep_duration_seconds, sleep_nap_seconds, - sleep_deep_seconds, sleep_light_seconds, - sleep_rem_seconds, sleep_awake_seconds, - sleep_rem_capable, sleep_score, sleep_feedback, - respiration_avg_waking, respiration_avg_sleep, - respiration_lowest, respiration_highest - ) VALUES ( - %s, - %s,%s,%s, - %s,%s,%s, - %s,%s,%s, - %s,%s, - %s,%s, - %s,%s, - %s,%s, - %s,%s, - %s,%s, - %s,%s, - %s, - %s,%s, - %s,%s, - %s, - %s,%s,%s, - %s,%s,%s, - %s,%s, - %s,%s, - %s,%s, - %s,%s, - %s,%s, - %s,%s,%s, - %s,%s,%s,%s, - %s,%s,%s, - %s,%s, - %s,%s - ) - ON CONFLICT (date) DO UPDATE SET - resting_hr = EXCLUDED.resting_hr, - min_hr = EXCLUDED.min_hr, - max_hr = EXCLUDED.max_hr, - total_steps = EXCLUDED.total_steps, - daily_step_goal = EXCLUDED.daily_step_goal, - total_distance_meters = EXCLUDED.total_distance_meters, - active_calories = EXCLUDED.active_calories, - total_calories = EXCLUDED.total_calories, - bmr_calories = EXCLUDED.bmr_calories, - highly_active_seconds = EXCLUDED.highly_active_seconds, - active_seconds = EXCLUDED.active_seconds, - sedentary_seconds = EXCLUDED.sedentary_seconds, - sleeping_seconds = EXCLUDED.sleeping_seconds, - moderate_intensity_minutes = EXCLUDED.moderate_intensity_minutes, - vigorous_intensity_minutes = EXCLUDED.vigorous_intensity_minutes, - floors_ascended = EXCLUDED.floors_ascended, - floors_descended = EXCLUDED.floors_descended, - average_stress = EXCLUDED.average_stress, - max_stress = EXCLUDED.max_stress, - rest_stress_duration = EXCLUDED.rest_stress_duration, - low_stress_duration = EXCLUDED.low_stress_duration, - medium_stress_duration = EXCLUDED.medium_stress_duration, - high_stress_duration = EXCLUDED.high_stress_duration, - stress_qualifier = EXCLUDED.stress_qualifier, - body_battery_highest = EXCLUDED.body_battery_highest, - body_battery_lowest = EXCLUDED.body_battery_lowest, - body_battery_most_recent = EXCLUDED.body_battery_most_recent, - body_battery_at_wake = EXCLUDED.body_battery_at_wake, - body_battery_during_sleep = EXCLUDED.body_battery_during_sleep, - average_spo2 = EXCLUDED.average_spo2, - lowest_spo2 = EXCLUDED.lowest_spo2, - latest_spo2 = EXCLUDED.latest_spo2, - hrv_weekly_avg = EXCLUDED.hrv_weekly_avg, - hrv_last_night = EXCLUDED.hrv_last_night, - hrv_last_night_5min_high = EXCLUDED.hrv_last_night_5min_high, - hrv_baseline_low = EXCLUDED.hrv_baseline_low, - hrv_baseline_high = EXCLUDED.hrv_baseline_high, - hrv_status = EXCLUDED.hrv_status, - hrv_feedback = EXCLUDED.hrv_feedback, - sleep_start_local = EXCLUDED.sleep_start_local, - sleep_end_local = EXCLUDED.sleep_end_local, - sleep_duration_seconds = EXCLUDED.sleep_duration_seconds, - sleep_nap_seconds = EXCLUDED.sleep_nap_seconds, - sleep_deep_seconds = EXCLUDED.sleep_deep_seconds, - sleep_light_seconds = EXCLUDED.sleep_light_seconds, - sleep_rem_seconds = EXCLUDED.sleep_rem_seconds, - sleep_awake_seconds = EXCLUDED.sleep_awake_seconds, - sleep_rem_capable = EXCLUDED.sleep_rem_capable, - sleep_score = EXCLUDED.sleep_score, - sleep_feedback = EXCLUDED.sleep_feedback, - respiration_avg_waking = EXCLUDED.respiration_avg_waking, - respiration_avg_sleep = EXCLUDED.respiration_avg_sleep, - respiration_lowest = EXCLUDED.respiration_lowest, - respiration_highest = EXCLUDED.respiration_highest - """, ( + values = ( check_date, - # get_stats stats.get("restingHeartRate"), stats.get("minHeartRate"), stats.get("maxHeartRate"), @@ -360,7 +281,6 @@ data: stats.get("averageSpo2"), stats.get("lowestSpo2"), stats.get("latestSpo2"), - # get_hrv_data hrv.get("weeklyAvg"), hrv.get("lastNight"), hrv.get("lastNight5MinHigh"), @@ -368,7 +288,6 @@ data: hrv.get("baselineBalancedLower"), hrv.get("status"), hrv.get("feedbackPhrase"), - # get_sleep_data sleep.get("sleepStartTimestampLocal"), sleep.get("sleepEndTimestampLocal"), sleep.get("sleepTimeSeconds"), @@ -380,13 +299,14 @@ data: sleep.get("deviceRemCapable"), scores.get("value"), scores.get("qualifierKey"), - # get_respiration_data resp.get("avgWakingRespirationValue"), resp.get("avgSleepRespirationValue"), resp.get("lowestRespirationValue"), resp.get("highestRespirationValue"), - )) + ) + assert len(values) == N, f"Mismatch: {len(values)} values vs {N} columns" + cur.execute(INSERT_SQL, values) conn.commit() synced += 1 check_date += timedelta(days=1)