This commit is contained in:
fuenmao
2026-09-22 16:55:13 +08:00
parent c1c86b56b6
commit f61b3ce3e7
2 changed files with 90 additions and 16 deletions
@@ -109,6 +109,7 @@ public sealed class HomeAssistantDeviceSwitchOutput
public string DeviceId { get; set; } = string.Empty;
public string EntityId { get; set; } = string.Empty;
public string State { get; set; } = string.Empty;
public bool? IsMuted { get; set; }
public string Message { get; set; } = string.Empty;
}
@@ -172,30 +172,86 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
};
}
var service = input.TurnOn ? "turn_on" : "turn_off";
var targetOn = input.TurnOn;
bool? targetMuted = null;
var service = targetOn ? "turn_on" : "turn_off";
if (domain == "media_player")
{
service = input.TurnOn ? "media_play" : "media_pause";
// Xiaomi Sound Pro 的 media_player state 会长期保持 playing,不能用它
// 判断声音是否开启;is_volume_muted 才是该实体可稳定回读的控制状态。
var currentMutedText = currentState.GetStringAttribute("is_volume_muted");
var currentMuted = bool.TryParse(currentMutedText, out var parsedMuted) && parsedMuted;
targetMuted = !currentMuted;
targetOn = !targetMuted.Value;
service = "volume_mute";
}
var serviceData = new Dictionary<string, object?>
{
["entity_id"] = targetEntityId
};
if (targetMuted.HasValue)
{
serviceData["is_volume_muted"] = targetMuted.Value;
}
await _homeAssistant.CallServiceAsync(domain, service, serviceData, cancellationToken);
var resultState = domain == "media_player"
? currentState.State
: (targetOn ? "on" : "off");
bool? resultMuted = null;
if (domain == "media_player")
{
var stateConfirmed = false;
for (var attempt = 0; attempt < 5; attempt++)
{
await Task.Delay(300, cancellationToken);
var refreshedStates = await _homeAssistant.GetStatesAsync(cancellationToken);
var refreshedState = refreshedStates.FirstOrDefault(s =>
string.Equals(s.EntityId, targetEntityId, StringComparison.OrdinalIgnoreCase));
if (refreshedState is null)
{
continue;
}
resultState = refreshedState.State;
var refreshedMutedText = refreshedState.GetStringAttribute("is_volume_muted");
resultMuted = bool.TryParse(refreshedMutedText, out var refreshedMuted)
? refreshedMuted
: null;
stateConfirmed = resultMuted == targetMuted;
if (stateConfirmed)
{
break;
}
}
if (!stateConfirmed)
{
return new HomeAssistantDeviceSwitchOutput
{
Success = false,
DeviceId = input.DeviceId,
EntityId = targetEntityId,
State = resultState,
IsMuted = resultMuted,
Message = $"音箱指令已发送,但静音状态未确认更新(当前:{(resultMuted.HasValue ? (resultMuted.Value ? "已静音" : "声音开启") : "未知")})"
};
}
}
return new HomeAssistantDeviceSwitchOutput
{
Success = true,
DeviceId = input.DeviceId,
EntityId = targetEntityId,
State = domain == "media_player"
? (input.TurnOn ? "playing" : "paused")
: (input.TurnOn ? "on" : "off"),
State = resultState,
IsMuted = resultMuted,
Message = domain == "media_player"
? $"已成功{(input.TurnOn ? "开始播放" : "暂停播放")}"
: $"已成功{(input.TurnOn ? "开启" : "关闭")}设备"
? (targetMuted == true ? "音箱已静音" : "音箱声音已开启")
: $"已成功{(targetOn ? "开启" : "关闭")}设备"
};
}
catch (Exception ex)
@@ -384,12 +440,21 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
{
}
var successMessage = parameterType switch
{
"mattress_left_mode" when !string.Equals(serviceData["option"]?.ToString(), "无", StringComparison.Ordinal) =>
"左侧承托模式已启动;床垫共用气泵,右侧会自动停止",
"mattress_right_mode" when !string.Equals(serviceData["option"]?.ToString(), "无", StringComparison.Ordinal) =>
"右侧承托模式已启动;床垫共用气泵,左侧会自动停止",
_ => "参数调节成功"
};
return new HomeAssistantSetParameterOutput
{
Success = true,
DeviceId = input.DeviceId,
ParameterType = input.ParameterType ?? "",
Message = "参数调节成功"
Message = successMessage
};
}
catch (Exception ex)
@@ -539,13 +604,17 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
if (enableSpeakerSleepMode)
{
await RunAction(states, results, "开启音箱睡眠模式", entities.SpeakerSleepMode, "switch", "turn_on", null, cancellationToken);
await RunAction(states, results, "设置助眠音量", entities.Speaker, "media_player", "volume_set",
new Dictionary<string, object?> { ["volume_level"] = speakerVolume / 100d }, cancellationToken);
}
else
{
await RunAction(states, results, "关闭音箱睡眠模式", entities.SpeakerSleepMode, "switch", "turn_off", null, cancellationToken);
}
// 音箱声音与睡眠模式是两种不同能力。即使用户未启用睡眠模式,
// 一键舒睡也应取消静音并应用个性化音量。
await RunAction(states, results, "开启音箱声音", entities.Speaker, "media_player", "volume_mute",
new Dictionary<string, object?> { ["is_volume_muted"] = false }, cancellationToken);
await RunAction(states, results, "设置助眠音量", entities.Speaker, "media_player", "volume_set",
new Dictionary<string, object?> { ["volume_level"] = speakerVolume / 100d }, cancellationToken);
if (scene.EnableMattress)
{
@@ -571,6 +640,8 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
await RunAction(states, results, "关闭香氛灯", entities.FragranceLight, "light", "turn_off", null, cancellationToken);
await RunAction(states, results, "关闭助眠灯光", entities.AmbientLight, "light", "turn_off", null, cancellationToken);
await RunAction(states, results, "关闭音箱睡眠模式", entities.SpeakerSleepMode, "switch", "turn_off", null, cancellationToken);
await RunAction(states, results, "静音音箱", entities.Speaker, "media_player", "volume_mute",
new Dictionary<string, object?> { ["is_volume_muted"] = true }, cancellationToken);
if (_options.SleepScene.EnableMattress)
{
await RunAction(states, results, "关闭床垫左侧加热", entities.MattressLeftHeatingLevel, "number", "set_value",
@@ -835,7 +906,9 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
sceneEntityIds.Add(entities.FragranceSwitch);
if (!string.IsNullOrWhiteSpace(entities.AmbientLight))
sceneEntityIds.Add(entities.AmbientLight);
if (_options.SleepScene.EnableSpeakerSleepMode && !string.IsNullOrWhiteSpace(entities.SpeakerSleepMode))
if (!string.IsNullOrWhiteSpace(entities.Speaker))
sceneEntityIds.Add(entities.Speaker);
else if (_options.SleepScene.EnableSpeakerSleepMode && !string.IsNullOrWhiteSpace(entities.SpeakerSleepMode))
sceneEntityIds.Add(entities.SpeakerSleepMode);
if (_options.SleepScene.EnableMattress && !string.IsNullOrWhiteSpace(entities.MattressLeftHeatingLevel))
sceneEntityIds.Add(entities.MattressLeftHeatingLevel);
@@ -1114,17 +1187,17 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
});
}
if (enableSpeaker && !string.IsNullOrWhiteSpace(_options.Entities.SpeakerSleepMode))
if (!string.IsNullOrWhiteSpace(_options.Entities.Speaker))
{
var vol = userConfig?.SpeakerVolumePercent ?? 15;
presets.Add(new HomeAssistantScenePresetItem
{
DeviceName = "Xiaomi Sound Pro",
Category = nameof(HealthDeviceCategory.Media),
EntityId = _options.Entities.SpeakerSleepMode,
Action = "开启音箱睡眠模式",
Detail = $"切换睡眠模式开关 · 音量 {vol}%",
Parameters = new() { ["sleep_mode"] = "开启", ["volume"] = $"{vol}%" }
EntityId = _options.Entities.Speaker,
Action = enableSpeaker ? "开启音箱声音和睡眠模式" : "开启音箱声音",
Detail = enableSpeaker ? $"取消静音 · 开启睡眠模式 · 音量 {vol}%" : $"取消静音 · 音量 {vol}%",
Parameters = new() { ["muted"] = false, ["sleep_mode"] = enableSpeaker ? "开启" : "关闭", ["volume"] = $"{vol}%" }
});
}