fix(firmware): Retry ESP32 OTA service connections cleanly (#6081)

This commit is contained in:
Jeremiah K
2026-07-08 22:07:25 -05:00
committed by GitHub
parent bd14228fb6
commit c2a8e67aee
16 changed files with 1297 additions and 105 deletions

View File

@@ -22,6 +22,8 @@ import kotlinx.coroutines.SupervisorJob
import org.koin.core.annotation.Single
import org.meshtastic.core.common.util.handledLaunch
import org.meshtastic.core.common.util.ioDispatcher
import org.meshtastic.core.model.util.isOtaStatusNotification
import org.meshtastic.core.repository.FirmwareUpdateStatusRepository
import org.meshtastic.core.repository.FromRadioPacketHandler
import org.meshtastic.core.repository.LockdownCoordinator
import org.meshtastic.core.repository.MeshConfigFlowManager
@@ -54,6 +56,7 @@ class FromRadioPacketHandlerImpl(
private val packetHandler: PacketHandler,
private val notificationManager: NotificationManager,
private val lockdownCoordinator: LockdownCoordinator,
private val firmwareUpdateStatusRepository: FirmwareUpdateStatusRepository,
) : FromRadioPacketHandler {
// Application-scoped coroutine context for suspend work (e.g. getStringSuspend).
@@ -132,6 +135,11 @@ class FromRadioPacketHandlerImpl(
serviceStateWriter.setClientNotification(cn)
scope.handledLaunch {
if (cn.isOtaStatusNotification() && firmwareUpdateStatusRepository.status.value.isOtaUpdateActive) {
Logger.i { "OTA status ClientNotification received; skipping duplicate generic alert" }
return@handledLaunch
}
val inform = cn.key_verification_number_inform
val request = cn.key_verification_number_request
val verificationFinal = cn.key_verification_final

View File

@@ -21,6 +21,8 @@ import dev.mokkery.answering.returns
import dev.mokkery.every
import dev.mokkery.mock
import dev.mokkery.verify
import org.meshtastic.core.model.util.isOtaStatusNotification
import org.meshtastic.core.repository.FirmwareUpdateStatusRepository
import org.meshtastic.core.repository.MeshConfigFlowManager
import org.meshtastic.core.repository.MeshConfigHandler
import org.meshtastic.core.repository.MqttManager
@@ -43,6 +45,7 @@ import org.meshtastic.proto.QueueStatus
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
import org.meshtastic.proto.NodeInfo as ProtoNodeInfo
@@ -56,6 +59,7 @@ class FromRadioPacketHandlerImplTest {
private val configHandler: MeshConfigHandler = mock(MockMode.autofill)
private val xmodemManager: XModemManager = mock(MockMode.autofill)
private val lockdownCoordinator = FakeLockdownCoordinator()
private val firmwareUpdateStatusRepository = FirmwareUpdateStatusRepository()
private lateinit var handler: FromRadioPacketHandlerImpl
@@ -71,6 +75,7 @@ class FromRadioPacketHandlerImplTest {
packetHandler,
notificationManager,
lockdownCoordinator,
firmwareUpdateStatusRepository,
)
}
@@ -203,4 +208,22 @@ class FromRadioPacketHandlerImplTest {
verify { serviceRepository.setClientNotification(notification) }
}
@Test
fun `OTA status client notifications are identified`() {
assertTrue(ClientNotification(message = "Rebooting to WiFi OTA").isOtaStatusNotification())
assertTrue(ClientNotification(message = "OTA Loader does not support WiFi").isOtaStatusNotification())
assertTrue(
ClientNotification(message = "Cannot start OTA: OTA Loader partition not found.").isOtaStatusNotification(),
)
assertTrue(ClientNotification(message = "Unable to switch to the OTA partition.").isOtaStatusNotification())
}
@Test
fun `non OTA client notifications are not identified as OTA status`() {
assertFalse(ClientNotification(message = "test").isOtaStatusNotification())
assertFalse(ClientNotification(message = "Low battery").isOtaStatusNotification())
assertFalse(ClientNotification(message = "ROTATE credentials").isOtaStatusNotification())
assertFalse(ClientNotification(message = "Quota exceeded").isOtaStatusNotification())
}
}