TalerBleProtocol.kt
/*
* This file is part of GNU Taler
* (C) 2026 Taler Systems S.A.
*
* GNU Taler is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation; either version 3, or (at your option) any later version.
*
* GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
package net.taler.merchantpos.devices
import java.util.UUID
internal object TalerBleProtocol
{
const val NAME = "Taler NFC Terminal"
val SERVICE: UUID = UUID.fromString("04942ad3-8352-4da9-b0e8-22323c7f18b7")
val URL: UUID = UUID.fromString("829f9e88-206d-47f0-bd07-ada608deaa67")
val CONTROL: UUID = UUID.fromString("829f9e88-206d-47f0-bd07-ada608deaa68")
val STATUS: UUID = UUID.fromString("829f9e88-206d-47f0-bd07-ada608deaa69")
// The physical LCD also ships with the SIG URI characteristic in the same Taler service.
val STANDARD_URI: UUID = UUID.fromString("00002ab6-0000-1000-8000-00805f9b34fb")
const val TEST_URL = "https://taler.net/#display-test"
const val CAPACITY = 255
fun isBondedFirmwareName(name: String?) = name?.matches(Regex("Taler [0-9A-Fa-f]{6}")) == true
fun isCandidate(
name: String?,
services: List<UUID>,
) = name == NAME || isBondedFirmwareName(name) || SERVICE in services
}
/** One acknowledged write at a time. Never split URLs: firmware replaces on every write. */
internal class TalerBleUrlQueue
{
private var acknowledged: String? = null
private var inFlight: String? = null
private val target get() = if (tooLong) "" else desired
var desired = ""
var mtu = 23
val tooLong get() = desired.toByteArray(Charsets.UTF_8).size > minOf(TalerBleProtocol.CAPACITY, mtu - 3)
val settled get() = inFlight == null && acknowledged == target
val writing get() = inFlight != null
fun next(): ByteArray?
{
if (inFlight != null ||
acknowledged == target)
{
return null
}
inFlight = target
return target.toByteArray(Charsets.UTF_8)
}
fun complete(success: Boolean)
{
acknowledged = if (success) inFlight else null
inFlight = null
}
fun reset()
{
acknowledged = null
inFlight = null
mtu = 23
}
}