feat(infrared): choose layout template at creation time and improve grid key management

This commit is contained in:
2026-08-25 14:34:47 +08:00
parent 849b7e4aea
commit ec51ae1db7
8 changed files with 321 additions and 202 deletions
@@ -19,7 +19,8 @@ class CreateVirtualRemote(
displayName: String,
type: RemoteDeviceType,
brandModel: String? = null,
transmissionMode: TransmissionMode,
transmissionMode: TransmissionMode = if (type == RemoteDeviceType.CUSTOM) TransmissionMode.STATIC_SLOTS else TransmissionMode.DYNAMIC_SCRATCH,
layoutStyle: PanelLayoutStyle? = null,
): DomainResult<VirtualRemoteId> {
val trimmedName = displayName.trim()
if (trimmedName.isBlank()) {
@@ -28,12 +29,14 @@ class CreateVirtualRemote(
)
}
val templates = PresetCodeLibrary.getTemplatesForType(type, brandModel)
val quotaResult = repository.getKeyBindings(VirtualRemoteId("dummy")) // or compute from quota
// Obtain slot quota
// We calculate needed static slots:
val neededStaticSlots = if (transmissionMode == TransmissionMode.STATIC_SLOTS) templates.size else 0
val resolvedLayout = layoutStyle ?: VirtualRemote.defaultLayoutFor(type)
val resolvedTransMode = if (type == RemoteDeviceType.CUSTOM) {
TransmissionMode.STATIC_SLOTS
} else {
transmissionMode
}
val templates = PresetCodeLibrary.getTemplatesForType(type, brandModel, resolvedLayout)
val remoteId = VirtualRemoteId(UUID.randomUUID().toString().replace("-", "").take(16))
val now = clock.now()
@@ -46,7 +49,8 @@ class CreateVirtualRemote(
displayName = trimmedName,
type = type,
brandModel = brandModel,
transmissionMode = transmissionMode,
layoutStyle = resolvedLayout,
transmissionMode = resolvedTransMode,
dynamicScratchSlot = VirtualRemote.DEFAULT_DYNAMIC_SCRATCH_SLOT,
acState = initialAcState,
createdAtEpochMillis = now,
@@ -54,9 +58,7 @@ class CreateVirtualRemote(
)
// Build key bindings
val bindings = if (transmissionMode == TransmissionMode.STATIC_SLOTS) {
// Find currently used slots by querying quota
// For now, assign slots incrementally or check
val bindings = if (resolvedTransMode == TransmissionMode.STATIC_SLOTS) {
templates.mapIndexed { index, template ->
RemoteKeyBinding(
id = UUID.randomUUID().toString(),
@@ -64,7 +66,7 @@ class CreateVirtualRemote(
profileId = profileId,
buttonId = template.buttonId,
displayName = template.displayName,
assignedSlot = (index + 1).toString(), // Will be allocated based on allocator
assignedSlot = (index + 1).toString(),
rawHexCode = template.defaultHexCode,
status = if (template.defaultHexCode != null) InfraredKeyStatus.READY else InfraredKeyStatus.FAILED,
)
@@ -89,7 +91,7 @@ class CreateVirtualRemote(
}
// If static and has default hex codes, download to hardware slots
if (transmissionMode == TransmissionMode.STATIC_SLOTS) {
if (resolvedTransMode == TransmissionMode.STATIC_SLOTS) {
val codesToDownload = bindings.mapNotNull { b ->
if (b.assignedSlot != null && b.rawHexCode != null) {
InfraredCode(deviceName, b.assignedSlot, b.rawHexCode)
@@ -26,7 +26,7 @@ object PresetCodeLibrary {
PresetBrandOption("generic_fan", "通用风扇 (Generic Fan)", RemoteDeviceType.FAN),
// 自定义
PresetBrandOption("custom_blank", "自定义学习遥控器 (Custom)", RemoteDeviceType.CUSTOM),
PresetBrandOption("custom_blank", "自定义遥控器 (Custom)", RemoteDeviceType.CUSTOM),
)
data class PresetButtonTemplate(
@@ -35,24 +35,12 @@ object PresetCodeLibrary {
val defaultHexCode: String? = null,
)
fun getTemplatesForType(type: RemoteDeviceType, presetId: String? = null): List<PresetButtonTemplate> = when (type) {
RemoteDeviceType.TV -> listOf(
PresetButtonTemplate("TV_POWER", "电源", "0x00FF02FD"),
PresetButtonTemplate("TV_MUTE", "静音", "0x00FF04FB"),
PresetButtonTemplate("TV_SOURCE", "信号源", "0x00FF06F9"),
PresetButtonTemplate("TV_NAV_UP", "上", "0x00FF08F7"),
PresetButtonTemplate("TV_NAV_DOWN", "下", "0x00FF0AF5"),
PresetButtonTemplate("TV_NAV_LEFT", "左", "0x00FF0CF3"),
PresetButtonTemplate("TV_NAV_RIGHT", "右", "0x00FF0EF1"),
PresetButtonTemplate("TV_OK", "确定", "0x00FF10EF"),
PresetButtonTemplate("TV_HOME", "主页", "0x00FF12ED"),
PresetButtonTemplate("TV_BACK", "返回", "0x00FF14EB"),
PresetButtonTemplate("TV_MENU", "菜单", "0x00FF16E9"),
PresetButtonTemplate("TV_VOL_UP", "音量 +", "0x00FF18E7"),
PresetButtonTemplate("TV_VOL_DOWN", "音量 -", "0x00FF1AE5"),
PresetButtonTemplate("TV_CH_UP", "频道 +", "0x00FF1CE3"),
PresetButtonTemplate("TV_CH_DOWN", "频道 -", "0x00FF1EE1"),
)
fun getTemplatesForType(
type: RemoteDeviceType,
presetId: String? = null,
layoutStyle: PanelLayoutStyle? = null,
): List<PresetButtonTemplate> = when (type) {
RemoteDeviceType.TV -> getTvTemplates(hasHexCode = true)
RemoteDeviceType.AIR_CONDITIONER -> listOf(
PresetButtonTemplate("AC_POWER", "电源"),
PresetButtonTemplate("AC_TEMP_UP", "升温"),
@@ -70,12 +58,44 @@ object PresetCodeLibrary {
PresetButtonTemplate("FAN_TIMER", "定时", "0x01FE08F7"),
PresetButtonTemplate("FAN_MODE", "模式", "0x01FE0AF5"),
)
RemoteDeviceType.CUSTOM -> listOf(
PresetButtonTemplate("CUSTOM_POWER", "电源"),
PresetButtonTemplate("CUSTOM_KEY_1", "按键 1"),
PresetButtonTemplate("CUSTOM_KEY_2", "按键 2"),
PresetButtonTemplate("CUSTOM_KEY_3", "按键 3"),
PresetButtonTemplate("CUSTOM_KEY_4", "按键 4"),
)
RemoteDeviceType.CUSTOM -> when (layoutStyle) {
PanelLayoutStyle.TV_NAV -> getTvTemplates(hasHexCode = false)
PanelLayoutStyle.MEDIA_PLAYER -> getMediaPlayerTemplates(hasHexCode = false)
PanelLayoutStyle.AC_COMPACT -> emptyList()
PanelLayoutStyle.GRID, null -> listOf(
PresetButtonTemplate("CUSTOM_POWER", "电源"),
PresetButtonTemplate("CUSTOM_KEY_1", "按键 1"),
PresetButtonTemplate("CUSTOM_KEY_2", "按键 2"),
PresetButtonTemplate("CUSTOM_KEY_3", "按键 3"),
)
}
}
private fun getTvTemplates(hasHexCode: Boolean): List<PresetButtonTemplate> = listOf(
PresetButtonTemplate("TV_POWER", "电源", if (hasHexCode) "0x00FF02FD" else null),
PresetButtonTemplate("TV_MUTE", "静音", if (hasHexCode) "0x00FF04FB" else null),
PresetButtonTemplate("TV_SOURCE", "信号源", if (hasHexCode) "0x00FF06F9" else null),
PresetButtonTemplate("TV_NAV_UP", "上", if (hasHexCode) "0x00FF08F7" else null),
PresetButtonTemplate("TV_NAV_DOWN", "下", if (hasHexCode) "0x00FF0AF5" else null),
PresetButtonTemplate("TV_NAV_LEFT", "左", if (hasHexCode) "0x00FF0CF3" else null),
PresetButtonTemplate("TV_NAV_RIGHT", "右", if (hasHexCode) "0x00FF0EF1" else null),
PresetButtonTemplate("TV_OK", "确定", if (hasHexCode) "0x00FF10EF" else null),
PresetButtonTemplate("TV_HOME", "主页", if (hasHexCode) "0x00FF12ED" else null),
PresetButtonTemplate("TV_BACK", "返回", if (hasHexCode) "0x00FF14EB" else null),
PresetButtonTemplate("TV_MENU", "菜单", if (hasHexCode) "0x00FF16E9" else null),
PresetButtonTemplate("TV_VOL_UP", "音量 +", if (hasHexCode) "0x00FF18E7" else null),
PresetButtonTemplate("TV_VOL_DOWN", "音量 -", if (hasHexCode) "0x00FF1AE5" else null),
PresetButtonTemplate("TV_CH_UP", "频道 +", if (hasHexCode) "0x00FF1CE3" else null),
PresetButtonTemplate("TV_CH_DOWN", "频道 -", if (hasHexCode) "0x00FF1EE1" else null),
)
private fun getMediaPlayerTemplates(hasHexCode: Boolean): List<PresetButtonTemplate> = listOf(
PresetButtonTemplate("MEDIA_POWER", "电源", if (hasHexCode) "0x02FD02FD" else null),
PresetButtonTemplate("MEDIA_MUTE", "静音", if (hasHexCode) "0x02FD04FB" else null),
PresetButtonTemplate("MEDIA_PLAY_PAUSE", "播放/暂停", if (hasHexCode) "0x02FD06F9" else null),
PresetButtonTemplate("MEDIA_PREV", "上一曲", if (hasHexCode) "0x02FD08F7" else null),
PresetButtonTemplate("MEDIA_NEXT", "下一曲", if (hasHexCode) "0x02FD0AF5" else null),
PresetButtonTemplate("MEDIA_REWIND", "快退", if (hasHexCode) "0x02FD0CF3" else null),
PresetButtonTemplate("MEDIA_FF", "快进", if (hasHexCode) "0x02FD0EF1" else null),
)
}