111
This commit is contained in:
@@ -18,6 +18,7 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
|
||||
private readonly HomeAssistantRegistryService _registryService;
|
||||
private readonly HomeAssistantOptions _options;
|
||||
private readonly SqlSugarRepository<HealthSceneExecution> _executionRepository;
|
||||
private readonly SqlSugarRepository<HealthProfile> _profileRepository;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
public HealthHomeAssistantService(
|
||||
@@ -25,12 +26,14 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
|
||||
HomeAssistantRegistryService registryService,
|
||||
IOptions<HomeAssistantOptions> options,
|
||||
SqlSugarRepository<HealthSceneExecution> executionRepository,
|
||||
SqlSugarRepository<HealthProfile> profileRepository,
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
_homeAssistant = homeAssistant;
|
||||
_registryService = registryService;
|
||||
_options = options.Value;
|
||||
_executionRepository = executionRepository;
|
||||
_profileRepository = profileRepository;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
@@ -48,7 +51,22 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
|
||||
var summary = BuildSummary(discoveredDevices);
|
||||
var sceneDevices = BuildSceneDevicesSummary(stateMap);
|
||||
var environment = BuildEnvironmentOutput(stateMap, discoveredDevices);
|
||||
var scenePresets = BuildScenePresets(stateMap);
|
||||
|
||||
PersonalizedSceneConfig? userConfig = null;
|
||||
try
|
||||
{
|
||||
var userId = GetCurrentUserId();
|
||||
var profile = await _profileRepository.GetFirstAsync(p => p.UserId == userId);
|
||||
if (!string.IsNullOrWhiteSpace(profile?.CustomSceneConfigJson))
|
||||
{
|
||||
userConfig = JsonSerializer.Deserialize<PersonalizedSceneConfig>(profile.CustomSceneConfigJson);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
var scenePresets = BuildScenePresets(stateMap, userConfig);
|
||||
|
||||
return new HomeAssistantStatusOutput
|
||||
{
|
||||
@@ -59,7 +77,8 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
|
||||
Devices = discoveredDevices,
|
||||
SceneDevices = sceneDevices,
|
||||
Environment = environment,
|
||||
ScenePresets = scenePresets
|
||||
ScenePresets = scenePresets,
|
||||
ControlParameters = BuildControlParameterStates(stateMap)
|
||||
};
|
||||
}
|
||||
catch (Exception exception)
|
||||
@@ -73,7 +92,8 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
|
||||
Devices = [],
|
||||
SceneDevices = BuildSceneDevicesSummary(new Dictionary<string, HomeAssistantState>(StringComparer.OrdinalIgnoreCase)),
|
||||
Environment = new HomeAssistantEnvironmentOutput(),
|
||||
ScenePresets = BuildScenePresets(new Dictionary<string, HomeAssistantState>(StringComparer.OrdinalIgnoreCase))
|
||||
ScenePresets = BuildScenePresets(new Dictionary<string, HomeAssistantState>(StringComparer.OrdinalIgnoreCase)),
|
||||
ControlParameters = []
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -190,10 +210,220 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
|
||||
}
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost("/api/health/home-assistant/devices/set-parameter")]
|
||||
public async Task<HomeAssistantSetParameterOutput> SetParameter(
|
||||
[FromBody] HomeAssistantSetParameterInput input,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input.DeviceId) || string.IsNullOrWhiteSpace(input.ParameterType))
|
||||
{
|
||||
return new HomeAssistantSetParameterOutput
|
||||
{
|
||||
Success = false,
|
||||
DeviceId = input.DeviceId,
|
||||
ParameterType = input.ParameterType,
|
||||
Message = "未指定控制设备或参数"
|
||||
};
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var states = await _homeAssistant.GetStatesAsync(cancellationToken);
|
||||
var stateMap = states.ToDictionary(item => item.EntityId, StringComparer.OrdinalIgnoreCase);
|
||||
var registries = await _registryService.GetRegistriesAsync(cancellationToken);
|
||||
var discovered = BuildDiscoveredDevices(registries, stateMap);
|
||||
var targetDevice = discovered.FirstOrDefault(d =>
|
||||
string.Equals(d.DeviceId, input.DeviceId, StringComparison.OrdinalIgnoreCase));
|
||||
if (targetDevice is null)
|
||||
{
|
||||
return new HomeAssistantSetParameterOutput
|
||||
{
|
||||
Success = false,
|
||||
DeviceId = input.DeviceId,
|
||||
ParameterType = input.ParameterType,
|
||||
Message = "未找到指定设备"
|
||||
};
|
||||
}
|
||||
|
||||
var parameterType = input.ParameterType.Trim().ToLowerInvariant();
|
||||
string targetEntityId;
|
||||
string domain;
|
||||
string service;
|
||||
var serviceData = new Dictionary<string, object?>();
|
||||
switch (parameterType)
|
||||
{
|
||||
case "humidity":
|
||||
targetEntityId = _options.Entities.Dehumidifier;
|
||||
domain = "humidifier";
|
||||
service = "set_humidity";
|
||||
serviceData["humidity"] = ReadInt(input.Value, 40, 70, "目标湿度");
|
||||
break;
|
||||
case "fan_level":
|
||||
targetEntityId = _options.Entities.DehumidifierFanLevel;
|
||||
domain = "select";
|
||||
service = "select_option";
|
||||
var fanLevel = ReadString(input.Value).Replace('档', '挡');
|
||||
if (fanLevel is not ("1挡" or "2挡" or "3挡")) throw new InvalidOperationException("风速档位无效");
|
||||
serviceData["option"] = fanLevel;
|
||||
break;
|
||||
case "dehumidifier_mode":
|
||||
targetEntityId = _options.Entities.Dehumidifier;
|
||||
domain = "humidifier";
|
||||
service = "set_mode";
|
||||
var dehumidifierMode = ReadString(input.Value);
|
||||
if (dehumidifierMode is not ("睡眠" or "智能")) throw new InvalidOperationException("除湿模式无效");
|
||||
serviceData["mode"] = dehumidifierMode;
|
||||
break;
|
||||
case "brightness":
|
||||
targetEntityId = _options.Entities.AmbientLight;
|
||||
domain = "light";
|
||||
service = "turn_on";
|
||||
serviceData["brightness_pct"] = ReadInt(input.Value, 1, 100, "灯光亮度");
|
||||
break;
|
||||
case "color_temp":
|
||||
targetEntityId = _options.Entities.AmbientLight;
|
||||
domain = "light";
|
||||
service = "turn_on";
|
||||
serviceData["color_temp_kelvin"] = ReadInt(input.Value, 2200, 5000, "灯光色温");
|
||||
break;
|
||||
case "volume":
|
||||
targetEntityId = _options.Entities.Speaker;
|
||||
domain = "media_player";
|
||||
service = "volume_set";
|
||||
serviceData["volume_level"] = ReadInt(input.Value, 0, 100, "音箱音量") / 100d;
|
||||
break;
|
||||
case "speaker_sleep_mode":
|
||||
targetEntityId = _options.Entities.SpeakerSleepMode;
|
||||
domain = "switch";
|
||||
service = ReadBool(input.Value) ? "turn_on" : "turn_off";
|
||||
break;
|
||||
case "mattress_left_heating":
|
||||
targetEntityId = _options.Entities.MattressLeftHeatingLevel;
|
||||
domain = "number";
|
||||
service = "set_value";
|
||||
serviceData["value"] = ReadInt(input.Value, 0, 4, "床垫左侧加热档位");
|
||||
break;
|
||||
case "mattress_right_heating":
|
||||
targetEntityId = _options.Entities.MattressRightHeatingLevel;
|
||||
domain = "number";
|
||||
service = "set_value";
|
||||
serviceData["value"] = ReadInt(input.Value, 0, 4, "床垫右侧加热档位");
|
||||
break;
|
||||
case "mattress_left_mode":
|
||||
targetEntityId = _options.Entities.MattressLeftMode;
|
||||
domain = "select";
|
||||
service = "select_option";
|
||||
serviceData["option"] = ReadMattressMode(input.Value);
|
||||
break;
|
||||
case "mattress_right_mode":
|
||||
targetEntityId = _options.Entities.MattressRightMode;
|
||||
domain = "select";
|
||||
service = "select_option";
|
||||
serviceData["option"] = ReadMattressMode(input.Value);
|
||||
break;
|
||||
case "uv":
|
||||
targetEntityId = _options.Entities.DehumidifierUv;
|
||||
domain = "switch";
|
||||
service = ReadBool(input.Value) ? "turn_on" : "turn_off";
|
||||
break;
|
||||
default:
|
||||
return new HomeAssistantSetParameterOutput
|
||||
{
|
||||
Success = false,
|
||||
DeviceId = input.DeviceId,
|
||||
ParameterType = input.ParameterType,
|
||||
Message = $"不支持的参数类型: {input.ParameterType}"
|
||||
};
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(targetEntityId))
|
||||
throw new InvalidOperationException("该参数尚未配置 Home Assistant 实体");
|
||||
if (!string.IsNullOrWhiteSpace(input.EntityId) &&
|
||||
!string.Equals(input.EntityId, targetEntityId, StringComparison.OrdinalIgnoreCase))
|
||||
throw new InvalidOperationException("请求实体与该参数的受信任实体不匹配");
|
||||
if (!targetDevice.Entities.Any(e => string.Equals(e.EntityId, targetEntityId, StringComparison.OrdinalIgnoreCase)))
|
||||
throw new InvalidOperationException("该参数不属于指定设备");
|
||||
if (!stateMap.TryGetValue(targetEntityId, out var targetState) || !IsAvailable(targetState))
|
||||
throw new InvalidOperationException("目标参数实体当前离线或不可用");
|
||||
|
||||
serviceData["entity_id"] = targetEntityId;
|
||||
await _homeAssistant.CallServiceAsync(domain, service, serviceData, cancellationToken);
|
||||
|
||||
try
|
||||
{
|
||||
var userId = GetCurrentUserId();
|
||||
var profile = await _profileRepository.GetFirstAsync(p => p.UserId == userId);
|
||||
if (profile != null)
|
||||
{
|
||||
var customConfig = string.IsNullOrWhiteSpace(profile.CustomSceneConfigJson)
|
||||
? new PersonalizedSceneConfig()
|
||||
: JsonSerializer.Deserialize<PersonalizedSceneConfig>(profile.CustomSceneConfigJson) ?? new PersonalizedSceneConfig();
|
||||
|
||||
switch (parameterType)
|
||||
{
|
||||
case "humidity": customConfig.TargetHumidity = (int)serviceData["humidity"]!; break;
|
||||
case "fan_level": customConfig.FanLevel = (string)serviceData["option"]!; break;
|
||||
case "dehumidifier_mode": customConfig.DehumidifierMode = (string)serviceData["mode"]!; break;
|
||||
case "brightness": customConfig.LightBrightnessPercent = (int)serviceData["brightness_pct"]!; break;
|
||||
case "color_temp": customConfig.LightColorTemperatureKelvin = (int)serviceData["color_temp_kelvin"]!; break;
|
||||
case "volume": customConfig.SpeakerVolumePercent = ReadInt(input.Value, 0, 100, "音箱音量"); break;
|
||||
case "speaker_sleep_mode": customConfig.EnableSpeakerSleepMode = ReadBool(input.Value); break;
|
||||
case "uv": customConfig.EnableUv = ReadBool(input.Value); break;
|
||||
case "mattress_left_heating":
|
||||
case "mattress_right_heating": customConfig.MattressHeatingLevel = ReadInt(input.Value, 0, 4, "床垫加热档位"); break;
|
||||
case "mattress_left_mode":
|
||||
case "mattress_right_mode": customConfig.MattressMode = ReadMattressMode(input.Value); break;
|
||||
}
|
||||
|
||||
profile.CustomSceneConfigJson = JsonSerializer.Serialize(customConfig);
|
||||
await _profileRepository.UpdateAsync(profile);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return new HomeAssistantSetParameterOutput
|
||||
{
|
||||
Success = true,
|
||||
DeviceId = input.DeviceId,
|
||||
ParameterType = input.ParameterType ?? "",
|
||||
Message = "参数调节成功"
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new HomeAssistantSetParameterOutput
|
||||
{
|
||||
Success = false,
|
||||
DeviceId = input.DeviceId,
|
||||
ParameterType = input.ParameterType ?? "",
|
||||
Message = $"调节失败:{ex.Message}"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost("/api/health/home-assistant/scenes/sleep")]
|
||||
public Task<HomeAssistantSceneOutput> ExecuteSleepScene(CancellationToken cancellationToken)
|
||||
=> ExecuteScene("sleep", "舒睡模式", ExecuteSleepActions, cancellationToken);
|
||||
public async Task<HomeAssistantSceneOutput> ExecuteSleepScene(CancellationToken cancellationToken)
|
||||
{
|
||||
PersonalizedSceneConfig? customConfig = null;
|
||||
try
|
||||
{
|
||||
var userId = GetCurrentUserId();
|
||||
var profile = await _profileRepository.GetFirstAsync(p => p.UserId == userId);
|
||||
if (!string.IsNullOrWhiteSpace(profile?.CustomSceneConfigJson))
|
||||
{
|
||||
customConfig = JsonSerializer.Deserialize<PersonalizedSceneConfig>(profile.CustomSceneConfigJson);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return await ExecuteScene("sleep", "舒睡模式", (states, results, ct) => ExecuteSleepActions(states, results, customConfig, ct), cancellationToken);
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost("/api/health/home-assistant/scenes/all-off")]
|
||||
@@ -264,48 +494,69 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
|
||||
private async Task ExecuteSleepActions(
|
||||
IReadOnlyDictionary<string, HomeAssistantState> states,
|
||||
List<HomeAssistantActionResult> results,
|
||||
PersonalizedSceneConfig? customConfig,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var entities = _options.Entities;
|
||||
var scene = _options.SleepScene;
|
||||
|
||||
var targetHumidity = customConfig?.TargetHumidity ?? scene.TargetHumidity;
|
||||
var dehumMode = customConfig?.DehumidifierMode ?? scene.DehumidifierMode;
|
||||
var fanLevel = customConfig?.FanLevel ?? scene.FanLevel;
|
||||
var enableUv = customConfig?.EnableUv ?? scene.EnableUv;
|
||||
var lightBrightness = customConfig?.LightBrightnessPercent ?? scene.LightBrightnessPercent;
|
||||
var lightColorTemp = customConfig?.LightColorTemperatureKelvin ?? scene.LightColorTemperatureKelvin;
|
||||
var enableFragrance = customConfig?.EnableFragrance ?? scene.EnableFragrance;
|
||||
var enableSpeakerSleepMode = customConfig?.EnableSpeakerSleepMode ?? scene.EnableSpeakerSleepMode;
|
||||
var speakerVolume = customConfig?.SpeakerVolumePercent ?? 15;
|
||||
var mattressHeatingLevel = customConfig?.MattressHeatingLevel ?? scene.MattressHeatingLevel;
|
||||
var mattressMode = customConfig?.MattressMode ?? scene.MattressMode;
|
||||
|
||||
await RunAction(states, results, "开启除湿机", entities.Dehumidifier, "humidifier", "turn_on", null, cancellationToken);
|
||||
await RunAction(states, results, "设置目标湿度", entities.Dehumidifier, "humidifier", "set_humidity",
|
||||
new Dictionary<string, object?> { ["humidity"] = scene.TargetHumidity }, cancellationToken);
|
||||
new Dictionary<string, object?> { ["humidity"] = targetHumidity }, cancellationToken);
|
||||
await RunAction(states, results, "切换睡眠模式", entities.Dehumidifier, "humidifier", "set_mode",
|
||||
new Dictionary<string, object?> { ["mode"] = scene.DehumidifierMode }, cancellationToken);
|
||||
new Dictionary<string, object?> { ["mode"] = dehumMode }, cancellationToken);
|
||||
await RunAction(states, results, "设置低档风速", entities.DehumidifierFanLevel, "select", "select_option",
|
||||
new Dictionary<string, object?> { ["option"] = scene.FanLevel }, cancellationToken);
|
||||
await RunAction(states, results, scene.EnableUv ? "开启 UV 杀菌" : "关闭 UV 杀菌", entities.DehumidifierUv,
|
||||
"switch", scene.EnableUv ? "turn_on" : "turn_off", null, cancellationToken);
|
||||
new Dictionary<string, object?> { ["option"] = fanLevel }, cancellationToken);
|
||||
await RunAction(states, results, enableUv ? "开启 UV 杀菌" : "关闭 UV 杀菌", entities.DehumidifierUv,
|
||||
"switch", enableUv ? "turn_on" : "turn_off", null, cancellationToken);
|
||||
|
||||
if (scene.EnableFragrance)
|
||||
if (enableFragrance)
|
||||
{
|
||||
await RunAction(states, results, "开启香氛机", entities.FragranceSwitch, "switch", "turn_on", null, cancellationToken);
|
||||
await RunAction(states, results, "开启香氛灯", entities.FragranceLight, "light", "turn_on",
|
||||
new Dictionary<string, object?> { ["brightness_pct"] = scene.LightBrightnessPercent }, cancellationToken);
|
||||
new Dictionary<string, object?> { ["brightness_pct"] = lightBrightness }, cancellationToken);
|
||||
}
|
||||
|
||||
await RunAction(states, results, "开启助眠灯光", entities.AmbientLight, "light", "turn_on",
|
||||
new Dictionary<string, object?>
|
||||
{
|
||||
["brightness_pct"] = scene.LightBrightnessPercent,
|
||||
["color_temp_kelvin"] = scene.LightColorTemperatureKelvin
|
||||
["brightness_pct"] = lightBrightness,
|
||||
["color_temp_kelvin"] = lightColorTemp
|
||||
}, cancellationToken);
|
||||
|
||||
if (scene.EnableSpeakerSleepMode)
|
||||
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);
|
||||
}
|
||||
|
||||
if (scene.EnableMattress)
|
||||
{
|
||||
await RunAction(states, results, "设置床垫左侧低档加热", entities.MattressLeftHeatingLevel, "number", "set_value",
|
||||
new Dictionary<string, object?> { ["value"] = scene.MattressHeatingLevel }, cancellationToken);
|
||||
await RunAction(states, results, "设置床垫右侧低档加热", entities.MattressRightHeatingLevel, "number", "set_value",
|
||||
new Dictionary<string, object?> { ["value"] = scene.MattressHeatingLevel }, cancellationToken);
|
||||
await RunAction(states, results, "设置床垫左侧智能适配", entities.MattressLeftMode, "select", "select_option",
|
||||
new Dictionary<string, object?> { ["option"] = scene.MattressMode }, cancellationToken);
|
||||
await RunAction(states, results, "设置床垫右侧智能适配", entities.MattressRightMode, "select", "select_option",
|
||||
new Dictionary<string, object?> { ["option"] = scene.MattressMode }, cancellationToken);
|
||||
await RunAction(states, results, "设置床垫左侧加热档位", entities.MattressLeftHeatingLevel, "number", "set_value",
|
||||
new Dictionary<string, object?> { ["value"] = mattressHeatingLevel }, cancellationToken);
|
||||
await RunAction(states, results, "设置床垫右侧加热档位", entities.MattressRightHeatingLevel, "number", "set_value",
|
||||
new Dictionary<string, object?> { ["value"] = mattressHeatingLevel }, cancellationToken);
|
||||
await RunAction(states, results, "设置床垫左侧模式", entities.MattressLeftMode, "select", "select_option",
|
||||
new Dictionary<string, object?> { ["option"] = mattressMode }, cancellationToken);
|
||||
await RunAction(states, results, "设置床垫右侧模式", entities.MattressRightMode, "select", "select_option",
|
||||
new Dictionary<string, object?> { ["option"] = mattressMode }, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -733,10 +984,85 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
|
||||
return safe.Count > 0 ? safe : null;
|
||||
}
|
||||
|
||||
private List<HomeAssistantScenePresetItem> BuildScenePresets(
|
||||
private Dictionary<string, HomeAssistantParameterStateOutput> BuildControlParameterStates(
|
||||
IReadOnlyDictionary<string, HomeAssistantState> stateMap)
|
||||
{
|
||||
var scene = _options.SleepScene;
|
||||
var configured = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
["humidity"] = _options.Entities.Dehumidifier,
|
||||
["fan_level"] = _options.Entities.DehumidifierFanLevel,
|
||||
["uv"] = _options.Entities.DehumidifierUv,
|
||||
["brightness"] = _options.Entities.AmbientLight,
|
||||
["color_temp"] = _options.Entities.AmbientLight,
|
||||
["volume"] = _options.Entities.Speaker,
|
||||
["speaker_sleep_mode"] = _options.Entities.SpeakerSleepMode,
|
||||
["mattress_left_heating"] = _options.Entities.MattressLeftHeatingLevel,
|
||||
["mattress_right_heating"] = _options.Entities.MattressRightHeatingLevel,
|
||||
["mattress_left_mode"] = _options.Entities.MattressLeftMode,
|
||||
["mattress_right_mode"] = _options.Entities.MattressRightMode
|
||||
};
|
||||
var result = new Dictionary<string, HomeAssistantParameterStateOutput>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var (parameterType, entityId) in configured)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(entityId) || !stateMap.TryGetValue(entityId, out var state)) continue;
|
||||
result[parameterType] = new HomeAssistantParameterStateOutput
|
||||
{
|
||||
EntityId = entityId,
|
||||
State = state.State,
|
||||
Available = IsAvailable(state),
|
||||
Attributes = ExtractSafeAttributes(state)
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string ReadString(object? value)
|
||||
{
|
||||
if (value is JsonElement element)
|
||||
{
|
||||
return element.ValueKind == JsonValueKind.String
|
||||
? element.GetString()?.Trim() ?? string.Empty
|
||||
: element.ToString().Trim();
|
||||
}
|
||||
return value?.ToString()?.Trim() ?? string.Empty;
|
||||
}
|
||||
|
||||
private static int ReadInt(object? value, int min, int max, string label)
|
||||
{
|
||||
if (!int.TryParse(ReadString(value), out var parsed) || parsed < min || parsed > max)
|
||||
throw new InvalidOperationException($"{label}应在 {min} 到 {max} 之间");
|
||||
return parsed;
|
||||
}
|
||||
|
||||
private static bool ReadBool(object? value)
|
||||
{
|
||||
if (value is JsonElement element && element.ValueKind is JsonValueKind.True or JsonValueKind.False)
|
||||
return element.GetBoolean();
|
||||
if (bool.TryParse(ReadString(value), out var parsed)) return parsed;
|
||||
throw new InvalidOperationException("开关参数无效");
|
||||
}
|
||||
|
||||
private static string ReadMattressMode(object? value)
|
||||
{
|
||||
var mode = ReadString(value);
|
||||
if (mode is not ("无" or "瑜伽助眠" or "智能适配" or "智能腰托"))
|
||||
throw new InvalidOperationException("床垫模式无效");
|
||||
return mode;
|
||||
}
|
||||
|
||||
private List<HomeAssistantScenePresetItem> BuildScenePresets(
|
||||
IReadOnlyDictionary<string, HomeAssistantState> stateMap,
|
||||
PersonalizedSceneConfig? userConfig = null)
|
||||
{
|
||||
var defaultScene = _options.SleepScene;
|
||||
var targetHumidity = userConfig?.TargetHumidity ?? defaultScene.TargetHumidity;
|
||||
var dehumMode = userConfig?.DehumidifierMode ?? defaultScene.DehumidifierMode;
|
||||
var fanLevel = userConfig?.FanLevel ?? defaultScene.FanLevel;
|
||||
var enableUv = userConfig?.EnableUv ?? defaultScene.EnableUv;
|
||||
var lightBrightness = userConfig?.LightBrightnessPercent ?? defaultScene.LightBrightnessPercent;
|
||||
var lightColorTemp = userConfig?.LightColorTemperatureKelvin ?? defaultScene.LightColorTemperatureKelvin;
|
||||
var enableFragrance = userConfig?.EnableFragrance ?? defaultScene.EnableFragrance;
|
||||
var enableSpeaker = userConfig?.EnableSpeakerSleepMode ?? defaultScene.EnableSpeakerSleepMode;
|
||||
var presets = new List<HomeAssistantScenePresetItem>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(_options.Entities.Dehumidifier))
|
||||
@@ -747,18 +1073,18 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
|
||||
Category = nameof(HealthDeviceCategory.EnvironmentControl),
|
||||
EntityId = _options.Entities.Dehumidifier,
|
||||
Action = "开启恒湿舒睡",
|
||||
Detail = $"目标湿度 {scene.TargetHumidity}% · {scene.DehumidifierMode}模式 · 风速{scene.FanLevel} · UV{(scene.EnableUv ? "开" : "关")}",
|
||||
Detail = $"目标湿度 {targetHumidity}% · {dehumMode}模式 · 风速{fanLevel} · UV{(enableUv ? "开" : "关")}",
|
||||
Parameters = new()
|
||||
{
|
||||
["target_humidity"] = $"{scene.TargetHumidity}%",
|
||||
["mode"] = scene.DehumidifierMode,
|
||||
["fan_level"] = scene.FanLevel,
|
||||
["uv"] = scene.EnableUv ? "开启" : "关闭"
|
||||
["target_humidity"] = $"{targetHumidity}%",
|
||||
["mode"] = dehumMode,
|
||||
["fan_level"] = fanLevel,
|
||||
["uv"] = enableUv ? "开启" : "关闭"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (scene.EnableFragrance && !string.IsNullOrWhiteSpace(_options.Entities.FragranceSwitch))
|
||||
if (enableFragrance && !string.IsNullOrWhiteSpace(_options.Entities.FragranceSwitch))
|
||||
{
|
||||
presets.Add(new HomeAssistantScenePresetItem
|
||||
{
|
||||
@@ -779,43 +1105,44 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
|
||||
Category = nameof(HealthDeviceCategory.Lighting),
|
||||
EntityId = _options.Entities.AmbientLight,
|
||||
Action = "开启暖光助眠",
|
||||
Detail = $"亮度 {scene.LightBrightnessPercent}% · 色温 {scene.LightColorTemperatureKelvin}K",
|
||||
Detail = $"亮度 {lightBrightness}% · 色温 {lightColorTemp}K",
|
||||
Parameters = new()
|
||||
{
|
||||
["brightness"] = $"{scene.LightBrightnessPercent}%",
|
||||
["color_temp"] = $"{scene.LightColorTemperatureKelvin}K"
|
||||
["brightness"] = $"{lightBrightness}%",
|
||||
["color_temp"] = $"{lightColorTemp}K"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (scene.EnableSpeakerSleepMode && !string.IsNullOrWhiteSpace(_options.Entities.SpeakerSleepMode))
|
||||
if (enableSpeaker && !string.IsNullOrWhiteSpace(_options.Entities.SpeakerSleepMode))
|
||||
{
|
||||
var vol = userConfig?.SpeakerVolumePercent ?? 15;
|
||||
presets.Add(new HomeAssistantScenePresetItem
|
||||
{
|
||||
DeviceName = "Xiaomi Sound Pro",
|
||||
Category = nameof(HealthDeviceCategory.Media),
|
||||
EntityId = _options.Entities.SpeakerSleepMode,
|
||||
Action = "开启音箱睡眠模式",
|
||||
Detail = "仅切换已确认的睡眠模式开关,不自动播放未知媒体内容",
|
||||
Parameters = new() { ["sleep_mode"] = "开启" }
|
||||
Detail = $"切换睡眠模式开关 · 音量 {vol}%",
|
||||
Parameters = new() { ["sleep_mode"] = "开启", ["volume"] = $"{vol}%" }
|
||||
});
|
||||
}
|
||||
|
||||
if (scene.EnableMattress && !string.IsNullOrWhiteSpace(_options.Entities.MattressLeftHeatingLevel))
|
||||
if (defaultScene.EnableMattress && !string.IsNullOrWhiteSpace(_options.Entities.MattressLeftHeatingLevel))
|
||||
{
|
||||
var heatingLevel = userConfig?.MattressHeatingLevel ?? defaultScene.MattressHeatingLevel;
|
||||
var mattressMode = userConfig?.MattressMode ?? defaultScene.MattressMode;
|
||||
presets.Add(new HomeAssistantScenePresetItem
|
||||
{
|
||||
DeviceName = "8H AI智能好睡眠床垫X",
|
||||
Category = nameof(HealthDeviceCategory.Sleep),
|
||||
EntityId = _options.Entities.MattressLeftHeatingLevel,
|
||||
Action = "开启低档加热与智能适配",
|
||||
Detail = $"左右加热 {scene.MattressHeatingLevel} 档 · 左右气袋模式 {scene.MattressMode}",
|
||||
Action = "开启分区加热与承托模式",
|
||||
Detail = $"左右加热 {heatingLevel} 档 · {mattressMode}",
|
||||
Parameters = new()
|
||||
{
|
||||
["left_heating_level"] = scene.MattressHeatingLevel,
|
||||
["right_heating_level"] = scene.MattressHeatingLevel,
|
||||
["left_mode"] = scene.MattressMode,
|
||||
["right_mode"] = scene.MattressMode
|
||||
["heating_level"] = heatingLevel,
|
||||
["mode"] = mattressMode
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user