package coffee.liz.rookery.bt import kotlinx.coroutines.flow.Flow /** Orientation quaternion; right-handed, +X red, +Y blue, +Z white (GAN convention). */ data class Quaternion(val x: Double, val y: Double, val z: Double, val w: Double) /** Coarse angular velocity per axis as reported by the cube (small signed integers). */ data class AngularVelocity(val x: Int, val y: Int, val z: Int) /** Corner/edge permutation and orientation as reported by the cube. */ data class CubeState(val cp: List, val co: List, val ep: List, val eo: List) sealed interface SmartCubeEvent { /** * A face turn in WCA notation ("R", "U'"). * [cubeTimestampMs] is the cube's own clock (null if unknown, e.g. a move recovered from history); * [hostTimestampMs] is the host's monotonic clock when the notification arrived. * [recovered] marks moves that were missed live and filled in from the cube's move history, * whose host timestamp is therefore not meaningful. * [afterGap] marks the first move released after giving up on moves the cube never re-sent: * some moves before it are missing, so trust the next [Facelets] over replaying moves. */ data class Move( val move: String, val cubeTimestampMs: Long?, val hostTimestampMs: Long, val serial: Int = -1, val recovered: Boolean = false, val afterGap: Boolean = false, ) : SmartCubeEvent /** Kociemba-style 54-char facelets in U R F D L B order. */ data class Facelets(val facelets: String, val serial: Int = -1, val state: CubeState? = null) : SmartCubeEvent data class Battery(val percent: Int) : SmartCubeEvent data class Hardware( val name: String?, val hardwareVersion: String?, val softwareVersion: String?, val productDate: String? = null, val gyroSupported: Boolean = false, ) : SmartCubeEvent data class Gyro(val quaternion: Quaternion, val velocity: AngularVelocity? = null) : SmartCubeEvent data object Disconnected : SmartCubeEvent } /** A connected smart cube. [events] is hot: collect it before issuing requests to see their replies. */ /** What a platform offers for finding and connecting smart cubes. */ interface CubeBackend { /** Nearby smart cubes, as they're heard. Collect to scan; cancel to stop. */ fun scan(): Flow /** [mac] is the cube's real MAC (needed for decryption) where the platform's [address] is something else. */ suspend fun connect(scope: kotlinx.coroutines.CoroutineScope, address: String, mac: String): SmartCube } interface SmartCube { val name: String? val address: String val events: Flow suspend fun requestFacelets() suspend fun requestBattery() suspend fun requestHardware() suspend fun resetToSolved() suspend fun disconnect() }