From 973efa34cc9304d97d346ada7348605344c30840 Mon Sep 17 00:00:00 2001 From: fuenmao <980740792@qq.com> Date: Thu, 24 Sep 2026 16:12:47 +0800 Subject: [PATCH] 111 --- .../Health/Models/HomeAssistantModels.cs | 9 -- .../Services/HealthHomeAssistantService.cs | 116 +++++++----------- 2 files changed, 47 insertions(+), 78 deletions(-) diff --git a/Evergreen.Health.Application/Health/Models/HomeAssistantModels.cs b/Evergreen.Health.Application/Health/Models/HomeAssistantModels.cs index afe1d3c..0723054 100644 --- a/Evergreen.Health.Application/Health/Models/HomeAssistantModels.cs +++ b/Evergreen.Health.Application/Health/Models/HomeAssistantModels.cs @@ -123,15 +123,6 @@ public sealed class HomeAssistantLightTransitionOutput public string Message { get; set; } = string.Empty; } -public sealed class HomeAssistantSpeakerDemoOutput -{ - public string AudioUrl { get; set; } = string.Empty; - public bool SpeakerOnline { get; set; } - public bool DirectPlaybackSupported { get; set; } - public bool CommandAccepted { get; set; } - public string Message { get; set; } = string.Empty; -} - public sealed class HomeAssistantSetParameterInput { public string DeviceId { get; set; } = string.Empty; diff --git a/Evergreen.Health.Application/Health/Services/HealthHomeAssistantService.cs b/Evergreen.Health.Application/Health/Services/HealthHomeAssistantService.cs index a2d6b47..262df09 100644 --- a/Evergreen.Health.Application/Health/Services/HealthHomeAssistantService.cs +++ b/Evergreen.Health.Application/Health/Services/HealthHomeAssistantService.cs @@ -14,8 +14,6 @@ namespace Evergreen.Health.Application.Health.Services; public sealed class HealthHomeAssistantService : IDynamicApiController, ITransient { private const string HealthUserIdClaim = "health_user_id"; - private const int PlayMediaFeature = 512; - private const string DemoAudioUrl = "https://oss.myzxjgt.com/App.Live/uploads/%E9%9F%B3%E9%A2%91/2026-05-29/57a6a64d8dfa4a1bb876a15bb8fdfd24.mp3"; private readonly HomeAssistantClient _homeAssistant; private readonly HomeAssistantLightTransitionService _lightTransition; private readonly HomeAssistantRegistryService _registryService; @@ -73,69 +71,6 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie catch (Exception exception) { return new HomeAssistantLightTransitionOutput { Success = false, Message = exception.Message }; } } - [Authorize] - [HttpGet("/api/health/home-assistant/speaker/demo")] - public async Task GetSpeakerDemo(CancellationToken cancellationToken) - { - var result = new HomeAssistantSpeakerDemoOutput { AudioUrl = DemoAudioUrl }; - try - { - var entityId = _options.Entities.Speaker; - if (string.IsNullOrWhiteSpace(entityId)) - { - result.Message = "尚未配置音箱实体"; - return result; - } - - var state = (await _homeAssistant.GetStatesAsync(cancellationToken)) - .FirstOrDefault(item => string.Equals(item.EntityId, entityId, StringComparison.OrdinalIgnoreCase)); - result.SpeakerOnline = state is not null && IsAvailable(state); - if (!result.SpeakerOnline) - { - result.Message = "音箱当前离线或不可用"; - return result; - } - - var features = (long)(state!.GetDoubleAttribute("supported_features") ?? 0); - result.DirectPlaybackSupported = (features & PlayMediaFeature) != 0; - result.Message = result.DirectPlaybackSupported - ? "音箱声明支持指定音频播放,可以尝试 OSS 示例音频" - : "当前 Xiaomi Home 音箱实体不支持播放指定音频;OSS 链接可在浏览器试听,但暂不能投放到音箱"; - } - catch (Exception exception) - { - result.Message = $"音箱能力检测失败:{exception.Message}"; - } - - return result; - } - - [Authorize] - [HttpPost("/api/health/home-assistant/speaker/play-demo")] - public async Task PlaySpeakerDemo(CancellationToken cancellationToken) - { - var result = await GetSpeakerDemo(cancellationToken); - if (!result.SpeakerOnline || !result.DirectPlaybackSupported) return result; - - try - { - await _homeAssistant.CallServiceAsync("media_player", "play_media", new Dictionary - { - ["entity_id"] = _options.Entities.Speaker, - ["media_content_id"] = DemoAudioUrl, - ["media_content_type"] = "music" - }, cancellationToken); - result.CommandAccepted = true; - result.Message = "播放指令已发送,请确认音箱是否实际出声;接口接受不等于播放成功"; - } - catch (Exception exception) - { - result.Message = $"音箱未接受播放指令:{exception.Message}"; - } - - return result; - } - [Authorize] [HttpGet("/api/health/home-assistant/status")] public async Task GetStatus(CancellationToken cancellationToken) @@ -834,6 +769,42 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie new Dictionary { ["option"] = mattressFirmness }, cancellationToken); } } + // 软硬度调节可能占用床垫气泵;等这些指令完成后,最后启动按摩。 + await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken); + await RunAction(states, results, "停止床垫左侧按摩", entities.MattressLeftMassagePart, "select", "select_option", + new Dictionary { ["option"] = "无" }, cancellationToken); + await RunAction(states, results, "设置床垫轻柔按摩", entities.MattressMassageLevel, "select", "select_option", + new Dictionary { ["option"] = "轻柔" }, cancellationToken); + const string rightMassageActionName = "开启床垫右侧全身按摩"; + await RunAction(states, results, rightMassageActionName, entities.MattressRightMassagePart, "select", "select_option", + new Dictionary { ["option"] = "全身" }, cancellationToken); + + // HA 接受指令不等于设备保持该状态;此前曾在两秒后自动复位为“无”。 + var rightMassageAction = results.LastOrDefault(item => item.Name == rightMassageActionName); + if (rightMassageAction?.Status == "Succeeded") + { + await Task.Delay(TimeSpan.FromSeconds(3), cancellationToken); + try + { + var currentStates = await _homeAssistant.GetStatesAsync(cancellationToken); + var currentRightMassage = currentStates.FirstOrDefault(item => + string.Equals(item.EntityId, entities.MattressRightMassagePart, StringComparison.OrdinalIgnoreCase)); + if (currentRightMassage?.State != "全身") + { + rightMassageAction.Status = "Failed"; + rightMassageAction.Message = $"指令已发送,但右侧按摩未保持全身模式(当前:{currentRightMassage?.State ?? "未读取到状态"})"; + } + else + { + rightMassageAction.Message = "已确认右侧全身按摩保持开启"; + } + } + catch (Exception exception) when (exception is not OperationCanceledException) + { + rightMassageAction.Status = "Failed"; + rightMassageAction.Message = $"指令已发送,但无法确认右侧按摩状态:{exception.Message}"; + } + } } } @@ -851,6 +822,10 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie await RunAction(states, results, "关闭音箱睡眠模式", entities.SpeakerSleepMode, "switch", "turn_off", null, cancellationToken); await RunAction(states, results, "静音音箱", entities.Speaker, "media_player", "volume_mute", new Dictionary { ["is_volume_muted"] = true }, cancellationToken); + await RunAction(states, results, "关闭床垫右侧按摩", entities.MattressRightMassagePart, "select", "select_option", + new Dictionary { ["option"] = "无" }, cancellationToken); + await RunAction(states, results, "关闭床垫左侧按摩", entities.MattressLeftMassagePart, "select", "select_option", + new Dictionary { ["option"] = "无" }, cancellationToken); if (_options.SleepScene.EnableMattress) { await RunAction(states, results, "关闭床垫左侧加热", entities.MattressLeftHeatingLevel, "number", "set_value", @@ -1466,12 +1441,14 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie DeviceName = "8H AI智能好睡眠床垫X", Category = nameof(HealthDeviceCategory.Sleep), EntityId = _options.Entities.MattressLeftHeatingLevel, - Action = "按天气和 BMI 调节床垫", - Detail = $"左右加热 {heatingLevel} 档 · 软硬度{firmness ?? "待填写 BMI"}", + Action = "按天气和 BMI 调节床垫并开启右侧按摩", + Detail = $"左右加热 {heatingLevel} 档 · 软硬度{firmness ?? "待填写 BMI"} · 右侧全身轻柔按摩", Parameters = new() { ["heating_level"] = heatingLevel, - ["firmness"] = firmness ?? "未自动调节" + ["firmness"] = firmness ?? "未自动调节", + ["right_massage_part"] = "全身", + ["massage_level"] = "轻柔" } }); } @@ -1488,5 +1465,6 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie } private static bool IsAvailable(HomeAssistantState state) - => state.State is not ("unavailable" or "unknown"); + => state.State != "unavailable" && + (state.State != "unknown" || state.EntityId.StartsWith("button.", StringComparison.OrdinalIgnoreCase)); }