去掉老床垫相关功能
This commit is contained in:
@@ -2,7 +2,7 @@ using Evergreen.Health.Application.Health.Integrations.HomeAssistant;
|
||||
|
||||
namespace Evergreen.Health.Application.Health.Devices.Contracts;
|
||||
|
||||
public enum DeviceCommandIntent { Normal, StartMassage, TurnRelaxationLightOn, TurnRelaxationLightOff }
|
||||
public enum DeviceCommandIntent { Normal = 0, TurnRelaxationLightOn = 2, TurnRelaxationLightOff = 3 }
|
||||
|
||||
/// <summary>实体由后端模块提供,不接受客户端任意实体或任意服务名。</summary>
|
||||
public sealed record DeviceCommand(string Name, string EntityId, string Domain, string Service,
|
||||
|
||||
@@ -29,9 +29,13 @@ public sealed class HomeAssistantDeviceCatalog
|
||||
.GroupBy(e => e.DeviceId!, StringComparer.OrdinalIgnoreCase)
|
||||
.ToDictionary(g => g.Key, g => g.ToList(), StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// 使用完整注册表识别退役设备;即使用于识别的实体被禁用,也不能重新发现同一设备。
|
||||
var excludedDevices = registries.Entities.Where(entity =>
|
||||
_options.ExcludedDeviceEntityIds.Contains(entity.EntityId, StringComparer.OrdinalIgnoreCase))
|
||||
.Select(entity => entity.DeviceId).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
// HA 将 Sun、Backup 等虚拟系统服务标记为 service,不属于家具或实体传感器。
|
||||
foreach (var device in registries.Devices.Where(device => string.IsNullOrWhiteSpace(device.DisabledBy) &&
|
||||
!string.Equals(device.EntryType, "service", StringComparison.OrdinalIgnoreCase)))
|
||||
!string.Equals(device.EntryType, "service", StringComparison.OrdinalIgnoreCase) && !excludedDevices.Contains(device.Id)))
|
||||
{
|
||||
var regEntities = entitiesByDevice.GetValueOrDefault(device.Id) ?? [];
|
||||
if (regEntities.Count == 0) continue;
|
||||
@@ -291,7 +295,6 @@ public sealed class HomeAssistantDeviceCatalog
|
||||
if (id.Contains("tvoc")) metrics["tvoc"] = entity.State;
|
||||
if (id.Contains("weight")) metrics["weight"] = entity.State;
|
||||
if (id.Contains("body_fat") || id.Contains("fat_rate")) metrics["bodyFatRate"] = entity.State;
|
||||
if (id.Contains("firmness") || id.Contains("soft")) metrics["firmness"] = entity.State;
|
||||
if (id.Contains("sleep_state") || id.Contains("sleep_status")) metrics["sleepStatus"] = entity.State;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ using Evergreen.Health.Application.Health.Devices.Discovery;
|
||||
using Evergreen.Health.Application.Health.Devices.Dehumidifier;
|
||||
using Evergreen.Health.Application.Health.Devices.Fragrance;
|
||||
using Evergreen.Health.Application.Health.Devices.Lighting;
|
||||
using Evergreen.Health.Application.Health.Devices.Mattress;
|
||||
using Evergreen.Health.Application.Health.Devices.Outlet;
|
||||
using Evergreen.Health.Application.Health.Integrations.HomeAssistant;
|
||||
using Evergreen.Health.Application.Health.Integrations.MiAirX;
|
||||
@@ -41,7 +40,7 @@ public static class HealthDeviceRegistration
|
||||
provider.GetRequiredService<IOptions<MiAirXOptions>>()));
|
||||
services.AddSingleton<DeviceActionRunner>();
|
||||
services.AddSingleton<SceneLightSafetyState>();
|
||||
services.AddSingleton<IDeviceActionSafetyRule, LightMassageSafetyRule>();
|
||||
services.AddSingleton<IDeviceActionSafetyRule, PrimaryLightStateRule>();
|
||||
services.AddSingleton<IndoorEnvironmentReader>();
|
||||
services.AddSingleton<EnvironmentModule>();
|
||||
services.AddSingleton<IHealthDeviceModule>(provider => provider.GetRequiredService<EnvironmentModule>());
|
||||
@@ -69,9 +68,6 @@ public static class HealthDeviceRegistration
|
||||
services.AddSingleton<IHealthDeviceModule>(provider => provider.GetRequiredService<LightingModule>());
|
||||
services.AddSingleton<AudioModule>();
|
||||
services.AddSingleton<IHealthDeviceModule>(provider => provider.GetRequiredService<AudioModule>());
|
||||
services.AddSingleton<MattressModule>();
|
||||
services.AddSingleton<IHealthDeviceModule>(provider => provider.GetRequiredService<MattressModule>());
|
||||
services.AddSingleton<ILightOffHandler>(provider => provider.GetRequiredService<MattressModule>());
|
||||
services.AddSingleton<LightOffDispatcher>();
|
||||
services.AddSingleton<DeviceModuleRegistry>(provider => new(
|
||||
provider.GetServices<IHealthDeviceModule>().Concat(
|
||||
|
||||
@@ -5,7 +5,7 @@ using Evergreen.Health.Application.Health.Scenes;
|
||||
|
||||
namespace Evergreen.Health.Application.Health.Devices.Lighting;
|
||||
|
||||
/// <summary>额外场景灯复用标准参数和单灯渐变;型号专属模式通过配置选择,不套用原灯的按摩/唤醒钩子。</summary>
|
||||
/// <summary>额外场景灯复用标准参数和单灯渐变;型号专属模式通过配置选择,不套用原灯的关灯事件/唤醒钩子。</summary>
|
||||
public sealed class ConfiguredSleepLightModule : HealthDeviceModule
|
||||
{
|
||||
private readonly SceneLightOptions _light;
|
||||
|
||||
@@ -38,7 +38,7 @@ public sealed class LightingModule : HealthDeviceModule
|
||||
|
||||
public override async Task ExecuteParameterAsync(DeviceCommand command, CancellationToken cancellationToken)
|
||||
{
|
||||
// 标准手动面板复用本灯的专属安全钩子;其他灯不会触发助眠灯/床垫联动。
|
||||
// 标准手动面板复用本灯的专属安全钩子;其他灯不会触发主灯关灯事件。
|
||||
command = command with { Intent = command.Service == "turn_off"
|
||||
? DeviceCommandIntent.TurnRelaxationLightOff : DeviceCommandIntent.TurnRelaxationLightOn };
|
||||
await _transition.CancelAsync(cancellationToken);
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
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
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user