fix: preserve infrared failure context
This commit is contained in:
@@ -49,6 +49,7 @@ class InfraredKeyFactory(
|
|||||||
sdkKey = "app_ir_$canonicalId",
|
sdkKey = "app_ir_$canonicalId",
|
||||||
status = InfraredKeyStatus.LEARNING,
|
status = InfraredKeyStatus.LEARNING,
|
||||||
lastErrorCode = null,
|
lastErrorCode = null,
|
||||||
|
lastErrorDetail = null,
|
||||||
createdAtEpochMillis = now,
|
createdAtEpochMillis = now,
|
||||||
updatedAtEpochMillis = now,
|
updatedAtEpochMillis = now,
|
||||||
),
|
),
|
||||||
|
|||||||
+9
-1
@@ -17,6 +17,13 @@ enum class InfraredKeyStatus {
|
|||||||
FAILED,
|
FAILED,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const val INTERRUPTED_LEARNING_ERROR_DETAIL =
|
||||||
|
"Interrupted learning result is unknown; retry required"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persisted infrared key state. [lastErrorDetail] may contain only fixed, domain-owned,
|
||||||
|
* sanitized text; raw SDK maps, tokens, codes, and vendor payloads must never be stored there.
|
||||||
|
*/
|
||||||
data class SavedInfraredKey(
|
data class SavedInfraredKey(
|
||||||
val id: InfraredKeyId,
|
val id: InfraredKeyId,
|
||||||
val profileId: ProviderProfileId,
|
val profileId: ProviderProfileId,
|
||||||
@@ -25,6 +32,7 @@ data class SavedInfraredKey(
|
|||||||
val sdkKey: String,
|
val sdkKey: String,
|
||||||
val status: InfraredKeyStatus,
|
val status: InfraredKeyStatus,
|
||||||
val lastErrorCode: String?,
|
val lastErrorCode: String?,
|
||||||
|
val lastErrorDetail: String?,
|
||||||
val createdAtEpochMillis: Long,
|
val createdAtEpochMillis: Long,
|
||||||
val updatedAtEpochMillis: Long,
|
val updatedAtEpochMillis: Long,
|
||||||
)
|
)
|
||||||
|
|||||||
+5
@@ -20,6 +20,7 @@ interface InfraredKeyRepository {
|
|||||||
expectedStatuses: Set<InfraredKeyStatus>,
|
expectedStatuses: Set<InfraredKeyStatus>,
|
||||||
targetStatus: InfraredKeyStatus,
|
targetStatus: InfraredKeyStatus,
|
||||||
lastErrorCode: String?,
|
lastErrorCode: String?,
|
||||||
|
lastErrorDetail: String?,
|
||||||
updatedAtEpochMillis: Long,
|
updatedAtEpochMillis: Long,
|
||||||
): DomainResult<Unit>
|
): DomainResult<Unit>
|
||||||
|
|
||||||
@@ -31,6 +32,10 @@ interface InfraredKeyRepository {
|
|||||||
|
|
||||||
suspend fun deleteIfNotLearning(ref: InfraredKeyRef): DomainResult<Unit>
|
suspend fun deleteIfNotLearning(ref: InfraredKeyRef): DomainResult<Unit>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomically marks this profile's interrupted LEARNING keys as FAILED, clears the vendor
|
||||||
|
* error code, stores [INTERRUPTED_LEARNING_ERROR_DETAIL], and updates their timestamp.
|
||||||
|
*/
|
||||||
suspend fun failInterruptedLearning(
|
suspend fun failInterruptedLearning(
|
||||||
profileId: ProviderProfileId,
|
profileId: ProviderProfileId,
|
||||||
updatedAtEpochMillis: Long,
|
updatedAtEpochMillis: Long,
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ class LearnInfraredKey(
|
|||||||
expectedStatuses = setOf(InfraredKeyStatus.LEARNING),
|
expectedStatuses = setOf(InfraredKeyStatus.LEARNING),
|
||||||
targetStatus = InfraredKeyStatus.READY,
|
targetStatus = InfraredKeyStatus.READY,
|
||||||
lastErrorCode = null,
|
lastErrorCode = null,
|
||||||
|
lastErrorDetail = null,
|
||||||
updatedAtEpochMillis = clock.now(),
|
updatedAtEpochMillis = clock.now(),
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
@@ -53,6 +54,7 @@ class LearnInfraredKey(
|
|||||||
expectedStatuses = setOf(InfraredKeyStatus.LEARNING),
|
expectedStatuses = setOf(InfraredKeyStatus.LEARNING),
|
||||||
targetStatus = InfraredKeyStatus.FAILED,
|
targetStatus = InfraredKeyStatus.FAILED,
|
||||||
lastErrorCode = result.error.externalCode,
|
lastErrorCode = result.error.externalCode,
|
||||||
|
lastErrorDetail = null,
|
||||||
updatedAtEpochMillis = clock.now(),
|
updatedAtEpochMillis = clock.now(),
|
||||||
)
|
)
|
||||||
result
|
result
|
||||||
@@ -90,6 +92,7 @@ class RetryInfraredKeyLearning(
|
|||||||
),
|
),
|
||||||
targetStatus = InfraredKeyStatus.LEARNING,
|
targetStatus = InfraredKeyStatus.LEARNING,
|
||||||
lastErrorCode = null,
|
lastErrorCode = null,
|
||||||
|
lastErrorDetail = null,
|
||||||
updatedAtEpochMillis = clock.now(),
|
updatedAtEpochMillis = clock.now(),
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
@@ -109,6 +112,7 @@ class RetryInfraredKeyLearning(
|
|||||||
expectedStatuses = setOf(InfraredKeyStatus.LEARNING),
|
expectedStatuses = setOf(InfraredKeyStatus.LEARNING),
|
||||||
targetStatus = InfraredKeyStatus.READY,
|
targetStatus = InfraredKeyStatus.READY,
|
||||||
lastErrorCode = null,
|
lastErrorCode = null,
|
||||||
|
lastErrorDetail = null,
|
||||||
updatedAtEpochMillis = clock.now(),
|
updatedAtEpochMillis = clock.now(),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -118,6 +122,7 @@ class RetryInfraredKeyLearning(
|
|||||||
expectedStatuses = setOf(InfraredKeyStatus.LEARNING),
|
expectedStatuses = setOf(InfraredKeyStatus.LEARNING),
|
||||||
targetStatus = InfraredKeyStatus.FAILED,
|
targetStatus = InfraredKeyStatus.FAILED,
|
||||||
lastErrorCode = result.error.externalCode,
|
lastErrorCode = result.error.externalCode,
|
||||||
|
lastErrorDetail = null,
|
||||||
updatedAtEpochMillis = clock.now(),
|
updatedAtEpochMillis = clock.now(),
|
||||||
)
|
)
|
||||||
result
|
result
|
||||||
|
|||||||
+5
@@ -22,6 +22,7 @@ data class InfraredStatusTransitionCall(
|
|||||||
val expectedStatuses: Set<InfraredKeyStatus>,
|
val expectedStatuses: Set<InfraredKeyStatus>,
|
||||||
val targetStatus: InfraredKeyStatus,
|
val targetStatus: InfraredKeyStatus,
|
||||||
val lastErrorCode: String?,
|
val lastErrorCode: String?,
|
||||||
|
val lastErrorDetail: String?,
|
||||||
val updatedAtEpochMillis: Long,
|
val updatedAtEpochMillis: Long,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -92,6 +93,7 @@ class InMemoryInfraredKeyRepository : InfraredKeyRepository {
|
|||||||
expectedStatuses: Set<InfraredKeyStatus>,
|
expectedStatuses: Set<InfraredKeyStatus>,
|
||||||
targetStatus: InfraredKeyStatus,
|
targetStatus: InfraredKeyStatus,
|
||||||
lastErrorCode: String?,
|
lastErrorCode: String?,
|
||||||
|
lastErrorDetail: String?,
|
||||||
updatedAtEpochMillis: Long,
|
updatedAtEpochMillis: Long,
|
||||||
): DomainResult<Unit> {
|
): DomainResult<Unit> {
|
||||||
val call = InfraredStatusTransitionCall(
|
val call = InfraredStatusTransitionCall(
|
||||||
@@ -99,6 +101,7 @@ class InMemoryInfraredKeyRepository : InfraredKeyRepository {
|
|||||||
expectedStatuses = expectedStatuses,
|
expectedStatuses = expectedStatuses,
|
||||||
targetStatus = targetStatus,
|
targetStatus = targetStatus,
|
||||||
lastErrorCode = lastErrorCode,
|
lastErrorCode = lastErrorCode,
|
||||||
|
lastErrorDetail = lastErrorDetail,
|
||||||
updatedAtEpochMillis = updatedAtEpochMillis,
|
updatedAtEpochMillis = updatedAtEpochMillis,
|
||||||
)
|
)
|
||||||
transitionCalls += call
|
transitionCalls += call
|
||||||
@@ -115,6 +118,7 @@ class InMemoryInfraredKeyRepository : InfraredKeyRepository {
|
|||||||
keys[ref] = existing.copy(
|
keys[ref] = existing.copy(
|
||||||
status = targetStatus,
|
status = targetStatus,
|
||||||
lastErrorCode = lastErrorCode,
|
lastErrorCode = lastErrorCode,
|
||||||
|
lastErrorDetail = lastErrorDetail,
|
||||||
updatedAtEpochMillis = updatedAtEpochMillis,
|
updatedAtEpochMillis = updatedAtEpochMillis,
|
||||||
)
|
)
|
||||||
publish()
|
publish()
|
||||||
@@ -166,6 +170,7 @@ class InMemoryInfraredKeyRepository : InfraredKeyRepository {
|
|||||||
key.copy(
|
key.copy(
|
||||||
status = InfraredKeyStatus.FAILED,
|
status = InfraredKeyStatus.FAILED,
|
||||||
lastErrorCode = null,
|
lastErrorCode = null,
|
||||||
|
lastErrorDetail = INTERRUPTED_LEARNING_ERROR_DETAIL,
|
||||||
updatedAtEpochMillis = updatedAtEpochMillis,
|
updatedAtEpochMillis = updatedAtEpochMillis,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+2
@@ -25,6 +25,8 @@ class InfraredKeyFactoryTest {
|
|||||||
assertEquals(id, result.value.id)
|
assertEquals(id, result.value.id)
|
||||||
assertEquals("app_ir_01j5molerabc123", result.value.sdkKey)
|
assertEquals("app_ir_01j5molerabc123", result.value.sdkKey)
|
||||||
assertEquals(InfraredKeyStatus.LEARNING, result.value.status)
|
assertEquals(InfraredKeyStatus.LEARNING, result.value.status)
|
||||||
|
assertEquals(null, result.value.lastErrorCode)
|
||||||
|
assertEquals(null, result.value.lastErrorDetail)
|
||||||
assertEquals("空调开关", result.value.displayName)
|
assertEquals("空调开关", result.value.displayName)
|
||||||
assertEquals(1_724_435_200_000L, result.value.createdAtEpochMillis)
|
assertEquals(1_724_435_200_000L, result.value.createdAtEpochMillis)
|
||||||
assertEquals(result.value.createdAtEpochMillis, result.value.updatedAtEpochMillis)
|
assertEquals(result.value.createdAtEpochMillis, result.value.updatedAtEpochMillis)
|
||||||
|
|||||||
+15
-1
@@ -39,6 +39,7 @@ class LearnInfraredKeyTest {
|
|||||||
assertEquals(listOf("insert:LEARNING", "hardware", "transition:READY"), events)
|
assertEquals(listOf("insert:LEARNING", "hardware", "transition:READY"), events)
|
||||||
assertEquals(InfraredKeyStatus.READY, repository.onlyKey().status)
|
assertEquals(InfraredKeyStatus.READY, repository.onlyKey().status)
|
||||||
assertEquals(null, repository.onlyKey().lastErrorCode)
|
assertEquals(null, repository.onlyKey().lastErrorCode)
|
||||||
|
assertEquals(null, repository.onlyKey().lastErrorDetail)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -51,6 +52,7 @@ class LearnInfraredKeyTest {
|
|||||||
assertEquals(gatewayError, (result as DomainResult.Failure).error)
|
assertEquals(gatewayError, (result as DomainResult.Failure).error)
|
||||||
assertEquals(InfraredKeyStatus.FAILED, repository.onlyKey().status)
|
assertEquals(InfraredKeyStatus.FAILED, repository.onlyKey().status)
|
||||||
assertEquals("20206", repository.onlyKey().lastErrorCode)
|
assertEquals("20206", repository.onlyKey().lastErrorCode)
|
||||||
|
assertEquals(null, repository.onlyKey().lastErrorDetail)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -99,6 +101,7 @@ class LearnInfraredKeyTest {
|
|||||||
assertEquals(gatewayError, (result as DomainResult.Failure).error)
|
assertEquals(gatewayError, (result as DomainResult.Failure).error)
|
||||||
assertEquals(InfraredKeyStatus.LEARNING, repository.onlyKey().status)
|
assertEquals(InfraredKeyStatus.LEARNING, repository.onlyKey().status)
|
||||||
assertEquals("20206", repository.transitionCalls.single().lastErrorCode)
|
assertEquals("20206", repository.transitionCalls.single().lastErrorCode)
|
||||||
|
assertEquals(null, repository.transitionCalls.single().lastErrorDetail)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -201,16 +204,23 @@ class LearnInfraredKeyTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun retryClearsPriorErrorWhileGatewayCallbackIsRunning() = runTest {
|
fun retryUsesSeparateTimestampsAndClearsErrorsBeforeHardware() = runTest {
|
||||||
val original = failedKey()
|
val original = failedKey()
|
||||||
repository.seed(original)
|
repository.seed(original)
|
||||||
gateway.onLearn = { _, _, _ ->
|
gateway.onLearn = { _, _, _ ->
|
||||||
assertEquals(InfraredKeyStatus.LEARNING, repository.onlyKey().status)
|
assertEquals(InfraredKeyStatus.LEARNING, repository.onlyKey().status)
|
||||||
assertEquals(null, repository.onlyKey().lastErrorCode)
|
assertEquals(null, repository.onlyKey().lastErrorCode)
|
||||||
|
assertEquals(null, repository.onlyKey().lastErrorDetail)
|
||||||
|
assertEquals(100L, repository.onlyKey().updatedAtEpochMillis)
|
||||||
DomainResult.Success(Unit)
|
DomainResult.Success(Unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(DomainResult.Success(Unit), retry(original.ref()))
|
assertEquals(DomainResult.Success(Unit), retry(original.ref()))
|
||||||
|
assertEquals(InfraredKeyStatus.READY, repository.onlyKey().status)
|
||||||
|
assertEquals(null, repository.onlyKey().lastErrorCode)
|
||||||
|
assertEquals(null, repository.onlyKey().lastErrorDetail)
|
||||||
|
assertEquals(101L, repository.onlyKey().updatedAtEpochMillis)
|
||||||
|
assertEquals(listOf(100L, 101L), repository.transitionCalls.map { it.updatedAtEpochMillis })
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -225,6 +235,7 @@ class LearnInfraredKeyTest {
|
|||||||
assertEquals(gatewayError, (result as DomainResult.Failure).error)
|
assertEquals(gatewayError, (result as DomainResult.Failure).error)
|
||||||
assertEquals(InfraredKeyStatus.FAILED, repository.onlyKey().status)
|
assertEquals(InfraredKeyStatus.FAILED, repository.onlyKey().status)
|
||||||
assertEquals("20206", repository.onlyKey().lastErrorCode)
|
assertEquals("20206", repository.onlyKey().lastErrorCode)
|
||||||
|
assertEquals(null, repository.onlyKey().lastErrorDetail)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -241,6 +252,7 @@ class LearnInfraredKeyTest {
|
|||||||
assertEquals(gatewayError, (result as DomainResult.Failure).error)
|
assertEquals(gatewayError, (result as DomainResult.Failure).error)
|
||||||
assertEquals(InfraredKeyStatus.LEARNING, repository.onlyKey().status)
|
assertEquals(InfraredKeyStatus.LEARNING, repository.onlyKey().status)
|
||||||
assertEquals("20206", repository.transitionCalls.last().lastErrorCode)
|
assertEquals("20206", repository.transitionCalls.last().lastErrorCode)
|
||||||
|
assertEquals(null, repository.transitionCalls.last().lastErrorDetail)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -268,6 +280,7 @@ class LearnInfraredKeyTest {
|
|||||||
sdkKey = sdkKey,
|
sdkKey = sdkKey,
|
||||||
status = InfraredKeyStatus.FAILED,
|
status = InfraredKeyStatus.FAILED,
|
||||||
lastErrorCode = "old-error",
|
lastErrorCode = "old-error",
|
||||||
|
lastErrorDetail = "old-detail",
|
||||||
createdAtEpochMillis = 1L,
|
createdAtEpochMillis = 1L,
|
||||||
updatedAtEpochMillis = 2L,
|
updatedAtEpochMillis = 2L,
|
||||||
)
|
)
|
||||||
@@ -275,6 +288,7 @@ class LearnInfraredKeyTest {
|
|||||||
private fun learningKey() = failedKey().copy(
|
private fun learningKey() = failedKey().copy(
|
||||||
status = InfraredKeyStatus.LEARNING,
|
status = InfraredKeyStatus.LEARNING,
|
||||||
lastErrorCode = null,
|
lastErrorCode = null,
|
||||||
|
lastErrorDetail = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+21
-2
@@ -46,6 +46,10 @@ class ManageInfraredKeyTest {
|
|||||||
gateway.sendResult = DomainResult.Failure(gatewayError)
|
gateway.sendResult = DomainResult.Failure(gatewayError)
|
||||||
|
|
||||||
assertEquals(gatewayError, (send(key.ref()) as DomainResult.Failure).error)
|
assertEquals(gatewayError, (send(key.ref()) as DomainResult.Failure).error)
|
||||||
|
assertEquals(
|
||||||
|
listOf(InfraredSendCall(profile, DeviceName("ir-1"), "app_ir_key1")),
|
||||||
|
gateway.sentKeys,
|
||||||
|
)
|
||||||
assertEquals(InfraredKeyStatus.READY, repository.onlyKey().status)
|
assertEquals(InfraredKeyStatus.READY, repository.onlyKey().status)
|
||||||
assertTrue(repository.transitionCalls.isEmpty())
|
assertTrue(repository.transitionCalls.isEmpty())
|
||||||
}
|
}
|
||||||
@@ -84,8 +88,12 @@ class ManageInfraredKeyTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun renameTrimsDisplayNameAndPreservesStableIdentity() = runTest {
|
fun renameTrimsDisplayNameAndPreservesStableIdentityAndErrors() = runTest {
|
||||||
val before = readyKey(displayName = "Power", sdkKey = "app_ir_power")
|
val before = readyKey(displayName = "Power", sdkKey = "app_ir_power").copy(
|
||||||
|
status = InfraredKeyStatus.FAILED,
|
||||||
|
lastErrorCode = "20206",
|
||||||
|
lastErrorDetail = "fixed-detail",
|
||||||
|
)
|
||||||
repository.seed(before)
|
repository.seed(before)
|
||||||
|
|
||||||
assertEquals(DomainResult.Success(Unit), rename(before.ref(), " 空调开关 "))
|
assertEquals(DomainResult.Success(Unit), rename(before.ref(), " 空调开关 "))
|
||||||
@@ -94,6 +102,8 @@ class ManageInfraredKeyTest {
|
|||||||
assertEquals("空调开关", after.displayName)
|
assertEquals("空调开关", after.displayName)
|
||||||
assertEquals(before.sdkKey, after.sdkKey)
|
assertEquals(before.sdkKey, after.sdkKey)
|
||||||
assertEquals(before.id, after.id)
|
assertEquals(before.id, after.id)
|
||||||
|
assertEquals(before.lastErrorCode, after.lastErrorCode)
|
||||||
|
assertEquals(before.lastErrorDetail, after.lastErrorDetail)
|
||||||
assertEquals(before.createdAtEpochMillis, after.createdAtEpochMillis)
|
assertEquals(before.createdAtEpochMillis, after.createdAtEpochMillis)
|
||||||
assertEquals(500L, after.updatedAtEpochMillis)
|
assertEquals(500L, after.updatedAtEpochMillis)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
@@ -172,6 +182,11 @@ class ManageInfraredKeyTest {
|
|||||||
val recovered = repository.allKeys().filter { it.profileId == profile && it.id.value in setOf("one", "two") }
|
val recovered = repository.allKeys().filter { it.profileId == profile && it.id.value in setOf("one", "two") }
|
||||||
assertTrue(recovered.all { it.status == InfraredKeyStatus.FAILED })
|
assertTrue(recovered.all { it.status == InfraredKeyStatus.FAILED })
|
||||||
assertTrue(recovered.all { it.lastErrorCode == null })
|
assertTrue(recovered.all { it.lastErrorCode == null })
|
||||||
|
assertTrue(
|
||||||
|
recovered.all {
|
||||||
|
it.lastErrorDetail == "Interrupted learning result is unknown; retry required"
|
||||||
|
},
|
||||||
|
)
|
||||||
assertTrue(recovered.all { it.updatedAtEpochMillis == 500L })
|
assertTrue(recovered.all { it.updatedAtEpochMillis == 500L })
|
||||||
assertEquals(InfraredKeyStatus.READY, repository.allKeys().single { it.id.value == "ready" }.status)
|
assertEquals(InfraredKeyStatus.READY, repository.allKeys().single { it.id.value == "ready" }.status)
|
||||||
assertEquals(InfraredKeyStatus.LEARNING, repository.allKeys().single { it.profileId == otherProfile }.status)
|
assertEquals(InfraredKeyStatus.LEARNING, repository.allKeys().single { it.profileId == otherProfile }.status)
|
||||||
@@ -206,6 +221,7 @@ class ManageInfraredKeyTest {
|
|||||||
sdkKey = sdkKey,
|
sdkKey = sdkKey,
|
||||||
status = InfraredKeyStatus.READY,
|
status = InfraredKeyStatus.READY,
|
||||||
lastErrorCode = null,
|
lastErrorCode = null,
|
||||||
|
lastErrorDetail = null,
|
||||||
createdAtEpochMillis = 1L,
|
createdAtEpochMillis = 1L,
|
||||||
updatedAtEpochMillis = 2L,
|
updatedAtEpochMillis = 2L,
|
||||||
)
|
)
|
||||||
@@ -213,15 +229,18 @@ class ManageInfraredKeyTest {
|
|||||||
private fun failedKey() = readyKey().copy(
|
private fun failedKey() = readyKey().copy(
|
||||||
status = InfraredKeyStatus.FAILED,
|
status = InfraredKeyStatus.FAILED,
|
||||||
lastErrorCode = "old-error",
|
lastErrorCode = "old-error",
|
||||||
|
lastErrorDetail = "old-detail",
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun learningKey(
|
private fun learningKey(
|
||||||
id: InfraredKeyId = InfraredKeyId("key1"),
|
id: InfraredKeyId = InfraredKeyId("key1"),
|
||||||
profileId: ProviderProfileId = profile,
|
profileId: ProviderProfileId = profile,
|
||||||
lastErrorCode: String? = null,
|
lastErrorCode: String? = null,
|
||||||
|
lastErrorDetail: String? = null,
|
||||||
) = readyKey(id = id, profileId = profileId).copy(
|
) = readyKey(id = id, profileId = profileId).copy(
|
||||||
status = InfraredKeyStatus.LEARNING,
|
status = InfraredKeyStatus.LEARNING,
|
||||||
lastErrorCode = lastErrorCode,
|
lastErrorCode = lastErrorCode,
|
||||||
|
lastErrorDetail = lastErrorDetail,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user