Files
Evergreen-Health/Evergreen.Health.Application/Health/Devices/Mattress/MattressModule.cs
T
2026-10-09 16:46:30 +08:00

121 lines
7.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Evergreen.Health.Application.Health.Devices.Contracts;
using Evergreen.Health.Application.Health.Integrations.HomeAssistant;
using Evergreen.Health.Application.Health.Models;
using Evergreen.Health.Application.Health.Scenes;
using Microsoft.Extensions.Options;
using Evergreen.Health.Application.Health.Services;
namespace Evergreen.Health.Application.Health.Devices.Mattress;
public sealed class MattressModule : HealthDeviceModule, ILightOffHandler
{
private readonly HomeAssistantOptions _options;
private HomeAssistantEntityOptions Entities => _options.Entities;
public MattressModule(DeviceActionRunner runner, IOptions<HomeAssistantOptions> options) : base(runner)
{
_options = options.Value;
var modes = new[] { "无", "瑜伽助眠", "智能适配", "智能腰托" };
var parts = new[] { "无", "背部", "腰部", "牵引", "腿部", "全身" };
var parameters = new List<DeviceParameterDefinition>
{
DeviceValues.Number("mattress_left_heating", Entities.MattressLeftHeatingLevel, 0, 4, "床垫左侧加热档位"),
DeviceValues.Number("mattress_right_heating", Entities.MattressRightHeatingLevel, 0, 4, "床垫右侧加热档位"),
DeviceValues.Select("mattress_left_mode", Entities.MattressLeftMode, modes, "床垫左侧承托模式", true),
DeviceValues.Select("mattress_right_mode", Entities.MattressRightMode, modes, "床垫右侧承托模式", true),
DeviceValues.Select("mattress_massage_left_part", Entities.MattressLeftMassagePart, parts, "左侧按摩部位",
intent: DeviceCommandIntent.StartMassage),
DeviceValues.Select("mattress_massage_right_part", Entities.MattressRightMassagePart, parts, "右侧按摩部位",
intent: DeviceCommandIntent.StartMassage),
DeviceValues.Select("mattress_massage_level", Entities.MattressMassageLevel, ["轻柔", "强劲"], "按摩力度")
};
foreach (var (zone, entityId) in Entities.MattressFirmness)
parameters.Add(DeviceValues.Select($"mattress_firmness_{zone}", entityId,
["最软", "很软", "稍软", "稍硬", "很硬", "最硬"], $"床垫{ZoneName(zone)}软硬度"));
Parameters = parameters;
}
public override string Key => "mattress";
public override string Category => nameof(HealthDeviceCategory.Sleep);
public override string DisplayName => "智能床垫";
public override string PrimaryEntityId => ""; // 床垫没有单一总开关,不能把加热实体冒充开关。
public override IReadOnlyList<DeviceParameterDefinition> Parameters { get; }
public override async Task ExecuteSleepAsync(DeviceSceneContext context, PersonalizedSceneConfig config,
string constitutionCode, CancellationToken cancellationToken)
{
if (!_options.SleepScene.EnableMattress) return;
await context.RunAsync(BuildParameterCommand("mattress_left_heating", config.MattressHeatingLevel), cancellationToken);
await context.RunAsync(BuildParameterCommand("mattress_right_heating", config.MattressHeatingLevel), cancellationToken);
var firmness = MattressComfortService.FirmnessName(config.MattressLeftFirmness);
if (firmness is null)
context.Results.Add(new() { Name = "按 BMI 调节床垫软硬度", Status = "Skipped",
Message = "健康档案缺少身高或体重,未自动修改软硬度" });
else
foreach (var zone in Entities.MattressFirmness.Keys)
await context.RunAsync(BuildParameterCommand($"mattress_firmness_{zone}", firmness), cancellationToken);
var modeCommand = BuildParameterCommand("mattress_left_mode", "智能适配");
await context.RunAsync(modeCommand, cancellationToken);
if (context.Results.Last().Status == "Succeeded")
await Runner.VerifyBatchAsync([modeCommand], context.Results, cancellationToken);
// 气泵调节结束后再启动按摩,保持真实设备所需的顺序。
await Runner.DelayAsync(TimeSpan.FromSeconds(2), cancellationToken);
await context.RunAsync(BuildParameterCommand("mattress_massage_left_part", "无"), cancellationToken);
await context.RunAsync(BuildParameterCommand("mattress_massage_level", "轻柔"), cancellationToken);
var massage = BuildParameterCommand("mattress_massage_right_part", "全身") with { ExpectedState = "全身" };
await context.RunAsync(massage, cancellationToken);
if (context.Results.Last().Status == "Succeeded")
{
await Runner.DelayAsync(TimeSpan.FromSeconds(3), cancellationToken);
await Runner.VerifyBatchAsync([massage], context.Results, cancellationToken);
}
}
public IEnumerable<DeviceCommand> MassageOffCommands() =>
[
new("关闭床垫右侧按摩", Entities.MattressRightMassagePart, "select", "select_option",
new Dictionary<string, object?> { ["option"] = "无" }, "无"),
new("关闭床垫左侧按摩", Entities.MattressLeftMassagePart, "select", "select_option",
new Dictionary<string, object?> { ["option"] = "无" }, "无")
];
public override IEnumerable<DeviceCommand> GetShutdownCommands()
{
foreach (var command in MassageOffCommands()) yield return command;
// 未参与自动启动不等于无需关闭:手动开启的加热也要随一键关闭停止。
yield return new("关闭床垫左侧加热", Entities.MattressLeftHeatingLevel, "number", "set_value",
new Dictionary<string, object?> { ["value"] = 0 }, "0");
yield return new("关闭床垫右侧加热", Entities.MattressRightHeatingLevel, "number", "set_value",
new Dictionary<string, object?> { ["value"] = 0 }, "0");
}
public async Task HandleAsync(CancellationToken cancellationToken)
{
var commands = MassageOffCommands().Where(command => !string.IsNullOrWhiteSpace(command.EntityId)).ToArray();
var results = new List<HomeAssistantActionResult>();
foreach (var command in commands)
await Runner.RunAsync(new Dictionary<string, HomeAssistantState>(), results, command, cancellationToken, false);
await Runner.VerifyBatchAsync(commands, results, cancellationToken);
if (results.Any(result => result.Status == "Failed"))
throw new InvalidOperationException("床垫按摩未确认关闭:" +
string.Join(";", results.Where(result => result.Status == "Failed").Select(result => result.Message)));
}
public override HomeAssistantScenePresetItem? BuildPreview(PersonalizedSceneConfig config, string constitutionCode)
{
if (!_options.SleepScene.EnableMattress || !EntityIds.Any()) return null;
var firmness = MattressComfortService.FirmnessName(config.MattressLeftFirmness) ?? "未自动调节";
var preview = Preview("按室温和 BMI 调节床垫并开启右侧按摩",
$"左右加热 {config.MattressHeatingLevel} 档 · 软硬度{firmness} · 右侧全身轻柔按摩",
new() { ["heating_level"] = config.MattressHeatingLevel, ["firmness"] = firmness,
["right_massage_part"] = "全身", ["massage_level"] = "轻柔" });
preview.EntityId = EntityIds.First();
return preview;
}
public static string ZoneName(string zone) => zone switch
{
"left_shoulder" => "左肩", "right_shoulder" => "右肩", "left_back" => "左背", "right_back" => "右背",
"left_waist" => "左腰", "right_waist" => "右腰", "left_lumbar" => "左腰椎", "right_lumbar" => "右腰椎",
"left_hip" => "左臀", "right_hip" => "右臀", "left_leg" => "左腿", "right_leg" => "右腿", _ => zone
};
}