namespace Evergreen.Health.Application.Health.Sleep; public enum BedPresence { Unknown, Empty, Occupied } public enum SleepDataAvailability { Unconfigured, Offline, Stale, Available } public enum ObservedSleepStage { Unknown, Awake, Light, Deep } /// 设备读数归一化入口;不凭心率、睡眠阶段 0 或未知值推断有人/无人。 public sealed record SleepObservation(string SourceId, DateTimeOffset? ObservedAt, SleepDataAvailability Availability, BedPresence Presence, ObservedSleepStage Stage = ObservedSleepStage.Unknown, double? HeartRate = null, double? RespiratoryRate = null, bool? Movement = null); public interface ISleepObservationSource { string Key { get; } Task ReadAsync(CancellationToken cancellationToken); } /// JE01 尚未接入,不启动轮询,也不将假数据视作“无人”。 public sealed class ReservedJe01Source : ISleepObservationSource { public string Key => "je01"; public Task ReadAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); return Task.FromResult(new SleepObservation(Key, null, SleepDataAvailability.Unconfigured, BedPresence.Unknown)); } } public sealed class SleepWorkflowOptions { public bool Enabled { get; init; } = false; public TimeSpan StableBedDuration { get; init; } = TimeSpan.FromSeconds(30); public TimeSpan PostGuidanceFadeDelay { get; init; } = TimeSpan.FromSeconds(30); public TimeSpan MaximumObservationAge { get; init; } = TimeSpan.FromMinutes(2); public TimeSpan SameAnomalyReminderInterval { get; init; } = TimeSpan.FromMinutes(30); public int GuidanceVolumePercent { get; init; } = 50; }