|
|
|
@@ -45,9 +45,10 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
|
|
|
|
|
var registries = await _registryService.GetRegistriesAsync(cancellationToken);
|
|
|
|
|
|
|
|
|
|
var discoveredDevices = BuildDiscoveredDevices(registries, stateMap);
|
|
|
|
|
var summary = BuildSummary(discoveredDevices, states);
|
|
|
|
|
var summary = BuildSummary(discoveredDevices);
|
|
|
|
|
var sceneDevices = BuildSceneDevicesSummary(stateMap);
|
|
|
|
|
var environment = BuildEnvironmentOutput(stateMap, discoveredDevices);
|
|
|
|
|
var scenePresets = BuildScenePresets(stateMap);
|
|
|
|
|
|
|
|
|
|
return new HomeAssistantStatusOutput
|
|
|
|
|
{
|
|
|
|
@@ -57,7 +58,8 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
|
|
|
|
|
Summary = summary,
|
|
|
|
|
Devices = discoveredDevices,
|
|
|
|
|
SceneDevices = sceneDevices,
|
|
|
|
|
Environment = environment
|
|
|
|
|
Environment = environment,
|
|
|
|
|
ScenePresets = scenePresets
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
catch (Exception exception)
|
|
|
|
@@ -69,8 +71,121 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
|
|
|
|
|
CheckedAt = DateTimeOffset.Now,
|
|
|
|
|
Summary = new HomeAssistantSummaryOutput(),
|
|
|
|
|
Devices = [],
|
|
|
|
|
SceneDevices = new HomeAssistantSceneDevicesSummaryOutput { Total = 4, Available = 0 },
|
|
|
|
|
Environment = new HomeAssistantEnvironmentOutput()
|
|
|
|
|
SceneDevices = BuildSceneDevicesSummary(new Dictionary<string, HomeAssistantState>(StringComparer.OrdinalIgnoreCase)),
|
|
|
|
|
Environment = new HomeAssistantEnvironmentOutput(),
|
|
|
|
|
ScenePresets = BuildScenePresets(new Dictionary<string, HomeAssistantState>(StringComparer.OrdinalIgnoreCase))
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Authorize]
|
|
|
|
|
[HttpPost("/api/health/home-assistant/devices/switch")]
|
|
|
|
|
public async Task<HomeAssistantDeviceSwitchOutput> SwitchDevice(
|
|
|
|
|
[FromBody] HomeAssistantDeviceSwitchInput input,
|
|
|
|
|
CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(input.DeviceId))
|
|
|
|
|
{
|
|
|
|
|
return new HomeAssistantDeviceSwitchOutput
|
|
|
|
|
{
|
|
|
|
|
Success = false,
|
|
|
|
|
Message = "未指定控制设备"
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var states = await _homeAssistant.GetStatesAsync(cancellationToken);
|
|
|
|
|
var stateMap = states.ToDictionary(s => s.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 || string.IsNullOrWhiteSpace(targetDevice.PrimaryControlEntityId))
|
|
|
|
|
{
|
|
|
|
|
return new HomeAssistantDeviceSwitchOutput
|
|
|
|
|
{
|
|
|
|
|
Success = false,
|
|
|
|
|
DeviceId = input.DeviceId,
|
|
|
|
|
Message = "该设备没有经过确认的主控开关"
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var targetEntityId = targetDevice.PrimaryControlEntityId;
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(input.EntityId) &&
|
|
|
|
|
!string.Equals(input.EntityId, targetEntityId, StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
return new HomeAssistantDeviceSwitchOutput
|
|
|
|
|
{
|
|
|
|
|
Success = false,
|
|
|
|
|
DeviceId = input.DeviceId,
|
|
|
|
|
EntityId = input.EntityId,
|
|
|
|
|
Message = "请求的实体不属于该设备的主控能力"
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var candidate = targetDevice.Entities.FirstOrDefault(e =>
|
|
|
|
|
string.Equals(e.EntityId, targetEntityId, StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
if (candidate is null || candidate.Domain is not ("switch" or "humidifier" or "light" or "media_player" or "climate"))
|
|
|
|
|
{
|
|
|
|
|
return new HomeAssistantDeviceSwitchOutput
|
|
|
|
|
{
|
|
|
|
|
Success = false,
|
|
|
|
|
DeviceId = input.DeviceId,
|
|
|
|
|
EntityId = targetEntityId,
|
|
|
|
|
Message = "该实体不支持安全的开关控制"
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var domain = candidate.Domain;
|
|
|
|
|
|
|
|
|
|
if (!stateMap.TryGetValue(targetEntityId, out var currentState) || !IsAvailable(currentState))
|
|
|
|
|
{
|
|
|
|
|
return new HomeAssistantDeviceSwitchOutput
|
|
|
|
|
{
|
|
|
|
|
Success = false,
|
|
|
|
|
DeviceId = input.DeviceId,
|
|
|
|
|
EntityId = targetEntityId,
|
|
|
|
|
Message = "目标设备当前处于离线状态"
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var service = input.TurnOn ? "turn_on" : "turn_off";
|
|
|
|
|
if (domain == "media_player")
|
|
|
|
|
{
|
|
|
|
|
service = input.TurnOn ? "media_play" : "media_pause";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var serviceData = new Dictionary<string, object?>
|
|
|
|
|
{
|
|
|
|
|
["entity_id"] = targetEntityId
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
await _homeAssistant.CallServiceAsync(domain, service, serviceData, cancellationToken);
|
|
|
|
|
|
|
|
|
|
return new HomeAssistantDeviceSwitchOutput
|
|
|
|
|
{
|
|
|
|
|
Success = true,
|
|
|
|
|
DeviceId = input.DeviceId,
|
|
|
|
|
EntityId = targetEntityId,
|
|
|
|
|
State = domain == "media_player"
|
|
|
|
|
? (input.TurnOn ? "playing" : "paused")
|
|
|
|
|
: (input.TurnOn ? "on" : "off"),
|
|
|
|
|
Message = domain == "media_player"
|
|
|
|
|
? $"已成功{(input.TurnOn ? "开始播放" : "暂停播放")}"
|
|
|
|
|
: $"已成功{(input.TurnOn ? "开启" : "关闭")}设备"
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
return new HomeAssistantDeviceSwitchOutput
|
|
|
|
|
{
|
|
|
|
|
Success = false,
|
|
|
|
|
DeviceId = input.DeviceId,
|
|
|
|
|
EntityId = input.EntityId ?? string.Empty,
|
|
|
|
|
Message = $"控制失败:{ex.Message}"
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -180,6 +295,18 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
|
|
|
|
|
|
|
|
|
|
if (scene.EnableSpeakerSleepMode)
|
|
|
|
|
await RunAction(states, results, "开启音箱睡眠模式", entities.SpeakerSleepMode, "switch", "turn_on", 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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task ExecuteAllOffActions(
|
|
|
|
@@ -193,6 +320,13 @@ 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);
|
|
|
|
|
if (_options.SleepScene.EnableMattress)
|
|
|
|
|
{
|
|
|
|
|
await RunAction(states, results, "关闭床垫左侧加热", entities.MattressLeftHeatingLevel, "number", "set_value",
|
|
|
|
|
new Dictionary<string, object?> { ["value"] = 0 }, cancellationToken);
|
|
|
|
|
await RunAction(states, results, "关闭床垫右侧加热", entities.MattressRightHeatingLevel, "number", "set_value",
|
|
|
|
|
new Dictionary<string, object?> { ["value"] = 0 }, cancellationToken);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task RunAction(
|
|
|
|
@@ -263,7 +397,7 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
|
|
|
|
|
|| (device.Model?.Contains("xiaomi", StringComparison.OrdinalIgnoreCase) == true)
|
|
|
|
|
|| regEntities.Any(e => string.Equals(e.Platform, "xiaomi_home", StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
|
|
|
|
|
if (!isXiaomi && regEntities.Count == 0)
|
|
|
|
|
if (!isXiaomi)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
@@ -302,10 +436,11 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
|
|
|
|
|
|
|
|
|
|
var areaName = !string.IsNullOrWhiteSpace(device.AreaId) && areaMap.TryGetValue(device.AreaId, out var an)
|
|
|
|
|
? an
|
|
|
|
|
: "睡眠空间";
|
|
|
|
|
: null;
|
|
|
|
|
|
|
|
|
|
var category = CategorizeDevice(displayName, device.Model ?? "", device.Manufacturer ?? "", entityOutputs);
|
|
|
|
|
var metrics = ExtractDeviceMetrics(category, entityOutputs);
|
|
|
|
|
var primaryControlEntityId = FindPrimaryControlEntityId(category, entityOutputs);
|
|
|
|
|
|
|
|
|
|
discovered.Add(new HomeAssistantDiscoveredDeviceOutput
|
|
|
|
|
{
|
|
|
|
@@ -316,6 +451,7 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
|
|
|
|
|
AreaName = areaName,
|
|
|
|
|
Category = category,
|
|
|
|
|
Online = isOnline,
|
|
|
|
|
PrimaryControlEntityId = primaryControlEntityId,
|
|
|
|
|
Entities = entityOutputs,
|
|
|
|
|
Metrics = metrics
|
|
|
|
|
});
|
|
|
|
@@ -357,6 +493,7 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
|
|
|
|
|
|
|
|
|
|
var online = entityOutputs.Any(e => e.Available);
|
|
|
|
|
var metrics = ExtractDeviceMetrics(category, entityOutputs);
|
|
|
|
|
var primaryControlEntityId = FindPrimaryControlEntityId(category, entityOutputs);
|
|
|
|
|
|
|
|
|
|
list.Add(new HomeAssistantDiscoveredDeviceOutput
|
|
|
|
|
{
|
|
|
|
@@ -367,6 +504,7 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
|
|
|
|
|
AreaName = area,
|
|
|
|
|
Category = category,
|
|
|
|
|
Online = online,
|
|
|
|
|
PrimaryControlEntityId = primaryControlEntityId,
|
|
|
|
|
Entities = entityOutputs,
|
|
|
|
|
Metrics = metrics
|
|
|
|
|
});
|
|
|
|
@@ -388,15 +526,42 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
|
|
|
|
|
return list;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string? FindPrimaryControlEntityId(
|
|
|
|
|
string category,
|
|
|
|
|
IReadOnlyList<HomeAssistantEntityOutput> entities)
|
|
|
|
|
{
|
|
|
|
|
var configuredEntityIds = category switch
|
|
|
|
|
{
|
|
|
|
|
nameof(HealthDeviceCategory.EnvironmentControl) => new[] { _options.Entities.Dehumidifier },
|
|
|
|
|
nameof(HealthDeviceCategory.Fragrance) => new[] { _options.Entities.FragranceSwitch },
|
|
|
|
|
nameof(HealthDeviceCategory.Lighting) => new[] { _options.Entities.AmbientLight, _options.Entities.FragranceLight },
|
|
|
|
|
nameof(HealthDeviceCategory.Media) => new[] { _options.Entities.Speaker },
|
|
|
|
|
_ => Array.Empty<string>()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
foreach (var configuredEntityId in configuredEntityIds.Where(id => !string.IsNullOrWhiteSpace(id)))
|
|
|
|
|
{
|
|
|
|
|
if (entities.Any(e => string.Equals(e.EntityId, configuredEntityId, StringComparison.OrdinalIgnoreCase)))
|
|
|
|
|
return configuredEntityId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return category switch
|
|
|
|
|
{
|
|
|
|
|
nameof(HealthDeviceCategory.EnvironmentControl) => entities.FirstOrDefault(e => e.Domain == "humidifier")?.EntityId,
|
|
|
|
|
nameof(HealthDeviceCategory.Lighting) => entities.FirstOrDefault(e => e.Domain == "light")?.EntityId,
|
|
|
|
|
nameof(HealthDeviceCategory.Media) => entities.FirstOrDefault(e => e.Domain == "media_player")?.EntityId,
|
|
|
|
|
_ => null
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static HomeAssistantSummaryOutput BuildSummary(
|
|
|
|
|
IReadOnlyList<HomeAssistantDiscoveredDeviceOutput> devices,
|
|
|
|
|
IReadOnlyList<HomeAssistantState> states)
|
|
|
|
|
IReadOnlyList<HomeAssistantDiscoveredDeviceOutput> devices)
|
|
|
|
|
{
|
|
|
|
|
var totalDevices = devices.Count;
|
|
|
|
|
var onlineDevices = devices.Count(d => d.Online);
|
|
|
|
|
var offlineDevices = totalDevices - onlineDevices;
|
|
|
|
|
var totalEntities = states.Count;
|
|
|
|
|
var availableEntities = states.Count(IsAvailable);
|
|
|
|
|
var totalEntities = devices.Sum(d => d.Entities.Count);
|
|
|
|
|
var availableEntities = devices.Sum(d => d.Entities.Count(e => e.Available));
|
|
|
|
|
|
|
|
|
|
return new HomeAssistantSummaryOutput
|
|
|
|
|
{
|
|
|
|
@@ -411,30 +576,27 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
|
|
|
|
|
private HomeAssistantSceneDevicesSummaryOutput BuildSceneDevicesSummary(IReadOnlyDictionary<string, HomeAssistantState> stateMap)
|
|
|
|
|
{
|
|
|
|
|
var entities = _options.Entities;
|
|
|
|
|
var sceneAvailableCount = 0;
|
|
|
|
|
var sceneEntityIds = new List<string>();
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(entities.Dehumidifier) &&
|
|
|
|
|
stateMap.TryGetValue(entities.Dehumidifier, out var sDehum) && IsAvailable(sDehum))
|
|
|
|
|
sceneAvailableCount++;
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(entities.Dehumidifier))
|
|
|
|
|
sceneEntityIds.Add(entities.Dehumidifier);
|
|
|
|
|
if (_options.SleepScene.EnableFragrance && !string.IsNullOrWhiteSpace(entities.FragranceSwitch))
|
|
|
|
|
sceneEntityIds.Add(entities.FragranceSwitch);
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(entities.AmbientLight))
|
|
|
|
|
sceneEntityIds.Add(entities.AmbientLight);
|
|
|
|
|
if (_options.SleepScene.EnableSpeakerSleepMode && !string.IsNullOrWhiteSpace(entities.SpeakerSleepMode))
|
|
|
|
|
sceneEntityIds.Add(entities.SpeakerSleepMode);
|
|
|
|
|
if (_options.SleepScene.EnableMattress && !string.IsNullOrWhiteSpace(entities.MattressLeftHeatingLevel))
|
|
|
|
|
sceneEntityIds.Add(entities.MattressLeftHeatingLevel);
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(entities.FragranceSwitch) &&
|
|
|
|
|
stateMap.TryGetValue(entities.FragranceSwitch, out var sFrag) && IsAvailable(sFrag))
|
|
|
|
|
sceneAvailableCount++;
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(entities.AmbientLight) &&
|
|
|
|
|
stateMap.TryGetValue(entities.AmbientLight, out var sLight) && IsAvailable(sLight))
|
|
|
|
|
sceneAvailableCount++;
|
|
|
|
|
|
|
|
|
|
var speakerOnline = (!string.IsNullOrWhiteSpace(entities.Speaker) &&
|
|
|
|
|
stateMap.TryGetValue(entities.Speaker, out var sSpk) && IsAvailable(sSpk))
|
|
|
|
|
|| (!string.IsNullOrWhiteSpace(entities.SpeakerSleepMode) &&
|
|
|
|
|
stateMap.TryGetValue(entities.SpeakerSleepMode, out var sSpkSleep) && IsAvailable(sSpkSleep));
|
|
|
|
|
if (speakerOnline) sceneAvailableCount++;
|
|
|
|
|
sceneEntityIds = sceneEntityIds.Distinct(StringComparer.OrdinalIgnoreCase).ToList();
|
|
|
|
|
var sceneAvailableCount = sceneEntityIds.Count(entityId =>
|
|
|
|
|
stateMap.TryGetValue(entityId, out var state) && IsAvailable(state));
|
|
|
|
|
|
|
|
|
|
return new HomeAssistantSceneDevicesSummaryOutput
|
|
|
|
|
{
|
|
|
|
|
Total = 4,
|
|
|
|
|
Available = Math.Min(4, sceneAvailableCount)
|
|
|
|
|
Total = sceneEntityIds.Count,
|
|
|
|
|
Available = sceneAvailableCount
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -470,6 +632,7 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
|
|
|
|
|
Temperature = GetVal(entities.Temperature, "temperature"),
|
|
|
|
|
Humidity = humidity,
|
|
|
|
|
Pm25 = GetVal(entities.Pm25, "pm25"),
|
|
|
|
|
Pm10 = GetVal(string.Empty, "pm10"),
|
|
|
|
|
Co2 = GetVal(entities.Co2, "co2"),
|
|
|
|
|
Hcho = GetVal(entities.Formaldehyde, "hcho"),
|
|
|
|
|
Tvoc = GetVal(entities.Tvoc, "tvoc")
|
|
|
|
@@ -570,6 +733,104 @@ public sealed class HealthHomeAssistantService : IDynamicApiController, ITransie
|
|
|
|
|
return safe.Count > 0 ? safe : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<HomeAssistantScenePresetItem> BuildScenePresets(
|
|
|
|
|
IReadOnlyDictionary<string, HomeAssistantState> stateMap)
|
|
|
|
|
{
|
|
|
|
|
var scene = _options.SleepScene;
|
|
|
|
|
var presets = new List<HomeAssistantScenePresetItem>();
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(_options.Entities.Dehumidifier))
|
|
|
|
|
{
|
|
|
|
|
presets.Add(new HomeAssistantScenePresetItem
|
|
|
|
|
{
|
|
|
|
|
DeviceName = "米家变频净化除湿机 Max",
|
|
|
|
|
Category = nameof(HealthDeviceCategory.EnvironmentControl),
|
|
|
|
|
EntityId = _options.Entities.Dehumidifier,
|
|
|
|
|
Action = "开启恒湿舒睡",
|
|
|
|
|
Detail = $"目标湿度 {scene.TargetHumidity}% · {scene.DehumidifierMode}模式 · 风速{scene.FanLevel} · UV{(scene.EnableUv ? "开" : "关")}",
|
|
|
|
|
Parameters = new()
|
|
|
|
|
{
|
|
|
|
|
["target_humidity"] = $"{scene.TargetHumidity}%",
|
|
|
|
|
["mode"] = scene.DehumidifierMode,
|
|
|
|
|
["fan_level"] = scene.FanLevel,
|
|
|
|
|
["uv"] = scene.EnableUv ? "开启" : "关闭"
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (scene.EnableFragrance && !string.IsNullOrWhiteSpace(_options.Entities.FragranceSwitch))
|
|
|
|
|
{
|
|
|
|
|
presets.Add(new HomeAssistantScenePresetItem
|
|
|
|
|
{
|
|
|
|
|
DeviceName = "米家智能香氛机",
|
|
|
|
|
Category = nameof(HealthDeviceCategory.Fragrance),
|
|
|
|
|
EntityId = _options.Entities.FragranceSwitch,
|
|
|
|
|
Action = "开启香氛释放",
|
|
|
|
|
Detail = "开启香氛机;香氛灯可用时同步开启柔光",
|
|
|
|
|
Parameters = new() { ["power"] = "开启" }
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(_options.Entities.AmbientLight))
|
|
|
|
|
{
|
|
|
|
|
presets.Add(new HomeAssistantScenePresetItem
|
|
|
|
|
{
|
|
|
|
|
DeviceName = "Lemesh 色温灯",
|
|
|
|
|
Category = nameof(HealthDeviceCategory.Lighting),
|
|
|
|
|
EntityId = _options.Entities.AmbientLight,
|
|
|
|
|
Action = "开启暖光助眠",
|
|
|
|
|
Detail = $"亮度 {scene.LightBrightnessPercent}% · 色温 {scene.LightColorTemperatureKelvin}K",
|
|
|
|
|
Parameters = new()
|
|
|
|
|
{
|
|
|
|
|
["brightness"] = $"{scene.LightBrightnessPercent}%",
|
|
|
|
|
["color_temp"] = $"{scene.LightColorTemperatureKelvin}K"
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (scene.EnableSpeakerSleepMode && !string.IsNullOrWhiteSpace(_options.Entities.SpeakerSleepMode))
|
|
|
|
|
{
|
|
|
|
|
presets.Add(new HomeAssistantScenePresetItem
|
|
|
|
|
{
|
|
|
|
|
DeviceName = "Xiaomi Sound Pro",
|
|
|
|
|
Category = nameof(HealthDeviceCategory.Media),
|
|
|
|
|
EntityId = _options.Entities.SpeakerSleepMode,
|
|
|
|
|
Action = "开启音箱睡眠模式",
|
|
|
|
|
Detail = "仅切换已确认的睡眠模式开关,不自动播放未知媒体内容",
|
|
|
|
|
Parameters = new() { ["sleep_mode"] = "开启" }
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (scene.EnableMattress && !string.IsNullOrWhiteSpace(_options.Entities.MattressLeftHeatingLevel))
|
|
|
|
|
{
|
|
|
|
|
presets.Add(new HomeAssistantScenePresetItem
|
|
|
|
|
{
|
|
|
|
|
DeviceName = "8H AI智能好睡眠床垫X",
|
|
|
|
|
Category = nameof(HealthDeviceCategory.Sleep),
|
|
|
|
|
EntityId = _options.Entities.MattressLeftHeatingLevel,
|
|
|
|
|
Action = "开启低档加热与智能适配",
|
|
|
|
|
Detail = $"左右加热 {scene.MattressHeatingLevel} 档 · 左右气袋模式 {scene.MattressMode}",
|
|
|
|
|
Parameters = new()
|
|
|
|
|
{
|
|
|
|
|
["left_heating_level"] = scene.MattressHeatingLevel,
|
|
|
|
|
["right_heating_level"] = scene.MattressHeatingLevel,
|
|
|
|
|
["left_mode"] = scene.MattressMode,
|
|
|
|
|
["right_mode"] = scene.MattressMode
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var preset in presets)
|
|
|
|
|
{
|
|
|
|
|
preset.Available = !string.IsNullOrWhiteSpace(preset.EntityId) &&
|
|
|
|
|
stateMap.TryGetValue(preset.EntityId, out var state) &&
|
|
|
|
|
IsAvailable(state);
|
|
|
|
|
preset.AvailabilityMessage = preset.Available ? "当前可执行" : "当前不可用,执行时跳过";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return presets;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool IsAvailable(HomeAssistantState state)
|
|
|
|
|
=> state.State is not ("unavailable" or "unknown");
|
|
|
|
|
}
|
|
|
|
|