Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 29 additions & 14 deletions internal/client/eightsleep.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,20 +376,35 @@ type SleepDay struct {
Date string `json:"day"`
Score float64 `json:"score"`
Tnt int `json:"tnt"`
Respiratory float64 `json:"respiratoryRate"`
HeartRate float64 `json:"heartRate"`
LatencyAsleep float64 `json:"latencyAsleepSeconds"`
LatencyOut float64 `json:"latencyOutSeconds"`
Duration float64 `json:"sleepDurationSeconds"`
Stages []Stage `json:"stages"`
SleepQuality struct {
HRV struct {
Score float64 `json:"score"`
} `json:"hrv"`
Resp struct {
Score float64 `json:"score"`
} `json:"respiratoryRate"`
} `json:"sleepQualityScore"`
Duration float64 `json:"sleepDuration"`
DeepDuration float64 `json:"deepDuration"`
RemDuration float64 `json:"remDuration"`
LightDuration float64 `json:"lightDuration"`
DeepPercent float64 `json:"deepPercent"`
RemPercent float64 `json:"remPercent"`
PresenceStart string `json:"presenceStart"`
PresenceEnd string `json:"presenceEnd"`
SleepStart string `json:"sleepStart"`
SleepEnd string `json:"sleepEnd"`
SleepQuality SleepQualityScore `json:"sleepQualityScore"`
}

// SleepQualityScore contains detailed sleep quality metrics from the API.
type SleepQualityScore struct {
Total float64 `json:"total"`
HRV SleepMetric `json:"hrv"`
HeartRate SleepMetric `json:"heartRate"`
Respiratory SleepMetric `json:"respiratoryRate"`
Deep SleepMetric `json:"deep"`
Rem SleepMetric `json:"rem"`
Waso SleepMetric `json:"waso"`
}

// SleepMetric represents a single sleep metric with current value and statistics.
type SleepMetric struct {
Current float64 `json:"current"`
Average float64 `json:"average"`
Score float64 `json:"score"`
}

// Stage represents sleep stage duration.
Expand Down
29 changes: 19 additions & 10 deletions internal/cmd/sleep.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,30 @@ var sleepDayCmd = &cobra.Command{
if err != nil {
return err
}
// Convert durations from seconds to hours for readability
durationHrs := day.Duration / 3600
deepHrs := day.DeepDuration / 3600
remHrs := day.RemDuration / 3600
lightHrs := day.LightDuration / 3600

rows := []map[string]any{
{
"date": day.Date,
"score": day.Score,
"tnt": day.Tnt,
"resp_rate": day.Respiratory,
"heart_rate": day.HeartRate,
"duration": day.Duration,
"latency_asleep": day.LatencyAsleep,
"latency_out": day.LatencyOut,
"hrv_score": day.SleepQuality.HRV.Score,
"date": day.Date,
"score": day.Score,
"duration_hrs": float64(int(durationHrs*10)) / 10,
"deep_hrs": float64(int(deepHrs*10)) / 10,
"rem_hrs": float64(int(remHrs*10)) / 10,
"light_hrs": float64(int(lightHrs*10)) / 10,
"sleep_start": day.SleepStart,
"sleep_end": day.SleepEnd,
"tnt": day.Tnt,
"rhr": day.SleepQuality.HeartRate.Current,
"hrv": day.SleepQuality.HRV.Current,
"resp_rate": day.SleepQuality.Respiratory.Current,
},
}
rows = output.FilterFields(rows, viper.GetStringSlice("fields"))
return output.Print(output.Format(viper.GetString("output")), []string{"date", "score", "duration", "latency_asleep", "latency_out", "tnt", "resp_rate", "heart_rate", "hrv_score"}, rows)
return output.Print(output.Format(viper.GetString("output")), []string{"date", "score", "duration_hrs", "deep_hrs", "rem_hrs", "light_hrs", "rhr", "hrv", "resp_rate", "tnt", "sleep_start", "sleep_end"}, rows)
},
}

Expand Down
26 changes: 16 additions & 10 deletions internal/cmd/sleep_range.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ var sleepRangeCmd = &cobra.Command{
if err := requireAuthFields(); err != nil {
return err
}
from := viper.GetString("from")
to := viper.GetString("to")
from, _ := cmd.Flags().GetString("from")
to, _ := cmd.Flags().GetString("to")
if from == "" || to == "" {
return fmt.Errorf("--from and --to are required")
}
Expand All @@ -47,18 +47,24 @@ var sleepRangeCmd = &cobra.Command{
if err != nil {
return err
}
// Convert durations from seconds to hours for readability
durationHrs := day.Duration / 3600
deepHrs := day.DeepDuration / 3600
remHrs := day.RemDuration / 3600

rows = append(rows, map[string]any{
"date": day.Date,
"score": day.Score,
"duration": day.Duration,
"tnt": day.Tnt,
"resp_rate": day.Respiratory,
"heart_rate": day.HeartRate,
"hrv_score": day.SleepQuality.HRV.Score,
"date": day.Date,
"score": day.Score,
"duration_hrs": float64(int(durationHrs*10)) / 10,
"deep_hrs": float64(int(deepHrs*10)) / 10,
"rem_hrs": float64(int(remHrs*10)) / 10,
"tnt": day.Tnt,
"rhr": day.SleepQuality.HeartRate.Current,
"hrv": day.SleepQuality.HRV.Current,
})
}
rows = output.FilterFields(rows, viper.GetStringSlice("fields"))
headers := []string{"date", "score", "duration", "tnt", "resp_rate", "heart_rate", "hrv_score"}
headers := []string{"date", "score", "duration_hrs", "deep_hrs", "rem_hrs", "rhr", "hrv", "tnt"}
if len(viper.GetStringSlice("fields")) > 0 {
headers = viper.GetStringSlice("fields")
}
Expand Down