add localonly & update apponly protobufs

pull/439/head
andrekir 2022-05-30 17:43:05 -03:00
rodzic d7aa79edc6
commit 875939931e
5 zmienionych plików z 107 dodań i 54 usunięć

Wyświetl plik

@ -1,16 +1,20 @@
package com.geeksville.mesh.model
import com.geeksville.mesh.ChannelProtos
import com.geeksville.mesh.ConfigProtos
import com.google.protobuf.ByteString
/** Utility function to make it easy to declare byte arrays - FIXME move someplace better */
fun byteArrayOfInts(vararg ints: Int) = ByteArray(ints.size) { pos -> ints[pos].toByte() }
fun xorHash(b: ByteArray) = b.fold(0) { acc, x -> acc xor (x.toInt() and 0xff) }
data class Channel(val settings: ChannelProtos.ChannelSettings) {
data class Channel(
val settings: ChannelProtos.ChannelSettings,
val loraConfig: ConfigProtos.Config.LoRaConfig
) {
companion object {
// These bytes must match the well known and not secret bytes used the default channel AES128 key device code
val channelDefaultKey = byteArrayOfInts(
private val channelDefaultKey = byteArrayOfInts(
0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59,
0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0x01
)
@ -19,19 +23,24 @@ data class Channel(val settings: ChannelProtos.ChannelSettings) {
private val defaultPSK =
byteArrayOfInts(1) // a shortstring code to indicate we need our default PSK
// TH=he unsecured channel that devices ship with
// The default channel that devices ship with
val default = Channel(
ChannelProtos.ChannelSettings.newBuilder()
// .setModemConfig(ChannelProtos.ChannelSettings.ModemConfig.LongFast)
.setPsk(ByteString.copyFrom(defaultPSK))
.build(),
ConfigProtos.Config.LoRaConfig.newBuilder()
.setModemPreset(ConfigProtos.Config.LoRaConfig.ModemPreset.LongFast)
.build()
)
}
/// Return the name of our channel as a human readable string. If empty string, assume "Default" per mesh.proto spec
val name: String
get() = settings.name.ifEmpty { "Placeholder" /*
when (settings.modemConfig) {
get() = settings.name.ifEmpty {
// We have a new style 'empty' channel name. Use the same logic from the device to convert that to a human readable name
if (loraConfig.bandwidth != 0)
"Unset"
else when (loraConfig.modemPreset) {
ConfigProtos.Config.LoRaConfig.ModemPreset.ShortFast -> "ShortFast"
ConfigProtos.Config.LoRaConfig.ModemPreset.ShortSlow -> "ShortSlow"
ConfigProtos.Config.LoRaConfig.ModemPreset.MidFast -> "MidFast"
@ -40,7 +49,7 @@ data class Channel(val settings: ChannelProtos.ChannelSettings) {
ConfigProtos.Config.LoRaConfig.ModemPreset.LongSlow -> "LongSlow"
ConfigProtos.Config.LoRaConfig.ModemPreset.VLongSlow -> "VLongSlow"
else -> "Invalid"
}*/
}
}
val psk: ByteString
@ -75,9 +84,13 @@ data class Channel(val settings: ChannelProtos.ChannelSettings) {
return "#${name}-${suffix}"
}
override fun equals(o: Any?): Boolean = (o is Channel)
&& psk.toByteArray() contentEquals o.psk.toByteArray()
&& name == o.name
}
override fun equals(other: Any?): Boolean = (other is Channel)
&& psk.toByteArray() contentEquals other.psk.toByteArray()
&& name == other.name
fun xorHash(b: ByteArray) = b.fold(0) { acc, x -> acc xor (x.toInt() and 0xff) }
override fun hashCode(): Int {
var result = settings.hashCode()
result = 31 * result + loraConfig.hashCode()
return result
}
}

Wyświetl plik

@ -15,7 +15,7 @@ data class ChannelSet(
) : Logging {
companion object {
const val prefix = "https://www.meshtastic.org/d/#"
const val prefix = "https://www.meshtastic.org/e/#"
private const val base64Flags = Base64.URL_SAFE + Base64.NO_WRAP + Base64.NO_PADDING
@ -42,7 +42,7 @@ data class ChannelSet(
val primaryChannel: Channel?
get() =
if (protobuf.settingsCount > 0)
Channel(protobuf.getSettings(0))
Channel(protobuf.getSettings(0), protobuf.loraConfig)
else
null
@ -54,10 +54,7 @@ data class ChannelSet(
val channelBytes = protobuf.toByteArray() ?: ByteArray(0) // if unset just use empty
val enc = Base64.encodeToString(channelBytes, base64Flags)
val p = if (upperCasePrefix)
prefix.uppercase()
else
prefix
val p = if (upperCasePrefix) prefix.uppercase() else prefix
return Uri.parse("$p$enc")
}

Wyświetl plik

@ -350,7 +350,7 @@ class MeshService : Service(), Logging {
var myNodeInfo: MyNodeInfo? = null
private var deviceConfig: ConfigProtos.Config? = null
private var localConfig: LocalOnlyProtos.LocalConfig = LocalOnlyProtos.LocalConfig.getDefaultInstance()
private var channels = fixupChannelList(listOf())
@ -472,6 +472,7 @@ class MeshService : Service(), Logging {
return AppOnlyProtos.ChannelSet.newBuilder().apply {
addAllSettings(cs)
loraConfig = localConfig.lora
}.build()
}
set(value) {
@ -489,6 +490,16 @@ class MeshService : Service(), Logging {
setChannel(it)
}
localConfig.let { currentConfig ->
val newConfig = ConfigProtos.Config.newBuilder()
val newPrefs = currentConfig.lora.toBuilder()
newPrefs.modemPreset = value.loraConfig.modemPreset
newConfig.lora = newPrefs.build()
sendDeviceConfig(newConfig.build())
}
channels = fixupChannelList(asChannels)
}
@ -497,7 +508,7 @@ class MeshService : Service(), Logging {
if (myNodeInfo == null)
throw RadioNotConnectedException()
from = myNodeNum
from = 0 // don't add myNodeNum
to = idNum
}
@ -726,9 +737,21 @@ class MeshService : Service(), Logging {
if (fromNodeNum == myNodeNum) {
when (a.variantCase) {
AdminProtos.AdminMessage.VariantCase.GET_CONFIG_RESPONSE -> {
debug("Admin: received deviceConfig")
deviceConfig = a.getConfigResponse
requestChannel(0) // Now start reading channels
val response = a.getConfigResponse
debug("Admin: received config ${response.payloadVariantCase}")
localConfig.let { currentConfig ->
val builder = currentConfig.toBuilder()
if (response.hasDevice()) builder.device = response.device
if (response.hasPosition()) builder.position = response.position
if (response.hasPower()) builder.power = response.power
if (response.hasWifi()) builder.wifi = response.wifi
if (response.hasDisplay()) builder.display = response.display
if (response.hasLora()) {
builder.lora = response.lora
requestChannel(0) // Now start reading channels
}
localConfig = builder.build()
}
}
AdminProtos.AdminMessage.VariantCase.GET_CHANNEL_RESPONSE -> {
@ -983,7 +1006,7 @@ class MeshService : Service(), Logging {
sleepTimeout = serviceScope.handledLaunch {
try {
// If we have a valid timeout, wait that long (+30 seconds) otherwise, just wait 30 seconds
val timeout = (deviceConfig?.power?.lsSecs ?: 0) + 30
val timeout = (localConfig.power?.lsSecs ?: 0) + 30
debug("Waiting for sleeping device, timeout=$timeout secs")
delay(timeout * 1000L)
@ -1075,7 +1098,7 @@ class MeshService : Service(), Logging {
private fun onRadioConnectionState(state: RadioServiceConnectionState) {
// sleep now disabled by default on ESP32, permanent is true unless isPowerSaving enabled
val lsEnabled = deviceConfig?.power?.isPowerSaving ?: false
val lsEnabled = localConfig.power?.isPowerSaving ?: false
val connected = state.isConnected
val permanent = state.isPermanent || !lsEnabled
onConnectionChanged(
@ -1249,7 +1272,7 @@ class MeshService : Service(), Logging {
regenMyNodeInfo()
// We'll need to get a new set of channels and settings now
deviceConfig = null
localConfig = LocalOnlyProtos.LocalConfig.getDefaultInstance()
// prefill the channel array with null channels
channels = fixupChannelList(listOf<ChannelProtos.Channel>())
@ -1259,8 +1282,8 @@ class MeshService : Service(), Logging {
private fun fixupChannelList(lIn: List<ChannelProtos.Channel>): Array<ChannelProtos.Channel> {
// When updating old firmware, we will briefly be told that there is zero channels
val maxChannels =
max(myNodeInfo?.maxChannels ?: 8, 8) // If we don't have my node info, assume 8 channels
var l = lIn
max(myNodeInfo?.maxChannels ?: 10, 10) // If we don't have my node info, assume 10 channels
val l = lIn.toMutableList()
while (l.size < maxChannels) {
val b = ChannelProtos.Channel.newBuilder()
b.index = l.size
@ -1272,15 +1295,15 @@ class MeshService : Service(), Logging {
private fun setRegionOnDevice() {
val curConfigRegion =
deviceConfig?.lora?.region ?: ConfigProtos.Config.LoRaConfig.RegionCode.Unset
localConfig.lora?.region ?: ConfigProtos.Config.LoRaConfig.RegionCode.Unset
if (curConfigRegion.number != curRegionValue && curRegionValue != ConfigProtos.Config.LoRaConfig.RegionCode.Unset_VALUE)
if (deviceVersion >= minFirmwareVersion) {
info("Telling device to upgrade region")
// Tell the device to set the new region field (old devices will simply ignore this)
deviceConfig?.let { currentConfig ->
val newConfig = currentConfig.toBuilder()
localConfig.let { currentConfig ->
val newConfig = ConfigProtos.Config.newBuilder()
val newPrefs = currentConfig.lora.toBuilder()
newPrefs.regionValue = curRegionValue
@ -1301,7 +1324,7 @@ class MeshService : Service(), Logging {
// Try to pull our region code from the new preferences field
// FIXME - do not check net - figuring out why board is rebooting
val curConfigRegion =
deviceConfig?.lora?.region ?: ConfigProtos.Config.LoRaConfig.RegionCode.Unset
localConfig.lora?.region ?: ConfigProtos.Config.LoRaConfig.RegionCode.Unset
if (curConfigRegion != ConfigProtos.Config.LoRaConfig.RegionCode.Unset) {
info("Using device region $curConfigRegion (code ${curConfigRegion.number})")
curRegionValue = curConfigRegion.number
@ -1364,9 +1387,11 @@ class MeshService : Service(), Logging {
}
private fun requestDeviceConfig() {
sendToRadio(newMeshPacketTo(myNodeNum).buildAdminPacket(wantResponse = true) {
getConfigRequest = AdminProtos.AdminMessage.ConfigType.DEVICE_CONFIG
})
AdminProtos.AdminMessage.ConfigType.values().forEach {
sendToRadio(newMeshPacketTo(myNodeNum).buildAdminPacket(wantResponse = true) {
if (it != AdminProtos.AdminMessage.ConfigType.UNRECOGNIZED) getConfigRequest = it
})
}
}
private fun requestChannel(channelIndex: Int) {
@ -1447,7 +1472,17 @@ class MeshService : Service(), Logging {
})
// Update our cached copy
this@MeshService.deviceConfig = c
localConfig.let { currentConfig ->
val builder = currentConfig.toBuilder()
if (c.hasDevice()) builder.device = c.device
if (c.hasPosition()) builder.position = c.position
if (c.hasPower()) builder.power = c.power
if (c.hasWifi()) builder.wifi = c.wifi
if (c.hasDisplay()) builder.display = c.display
if (c.hasLora()) builder.lora = c.lora
this@MeshService.localConfig = builder.build()
// debug("sendDeviceConfig: localConfig ${localConfig.toOneLineString()}")
}
}
/** Set our radio config
@ -1691,7 +1726,7 @@ class MeshService : Service(), Logging {
}
override fun getDeviceConfig(): ByteArray = toRemoteExceptions {
this@MeshService.deviceConfig?.toByteArray()
this@MeshService.localConfig.toByteArray()
?: throw NoDeviceConfigException()
}

Wyświetl plik

@ -115,7 +115,7 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
if (bitmap != null)
binding.qrView.setImageBitmap(bitmap)
val modemPreset = model.deviceConfig.value?.lora?.modemPreset
val modemPreset = channel.loraConfig.modemPreset
val channelOption = ChannelOption.fromConfig(modemPreset)
binding.filledExposedDropdown.setText(
getString(
@ -172,9 +172,17 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
}
/// Send new channel settings to the device
private fun installSettings(newChannel: ChannelProtos.ChannelSettings) {
private fun installSettings(
newChannel: ChannelProtos.ChannelSettings,
newLoRaConfig: ConfigProtos.Config.LoRaConfig
) {
val newSet =
ChannelSet(AppOnlyProtos.ChannelSet.newBuilder().addSettings(newChannel).build())
ChannelSet(
AppOnlyProtos.ChannelSet.newBuilder()
.addSettings(newChannel)
.setLoraConfig(newLoRaConfig)
.build()
)
// Try to change the radio, if it fails, tell the user why and throw away their edits
try {
model.setChannels(newSet)
@ -256,7 +264,7 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
}
.setPositiveButton(R.string.apply) { _, _ ->
debug("Switching back to default channel")
installSettings(Channel.default.settings)
installSettings(Channel.default.settings, Channel.default.loraConfig)
}
.show()
}
@ -302,15 +310,14 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
val newName = binding.channelNameEdit.text.toString().trim()
// Find the new modem config
val selectedChannelOptionString =
val selectedModemPresetString =
binding.filledExposedDropdown.editableText.toString()
var modemPreset = getModemPreset(selectedChannelOptionString)
// if (modemPreset == ConfigProtos.Config.LoRaConfig.ModemPreset.UNRECOGNIZED) // Huh? didn't find it - keep same
// modemPreset = oldPrimary.settings.modemConfig -> TODO add from LoraConfig.ModemPreset?
var newModemPreset = getModemPreset(selectedModemPresetString)
if (newModemPreset == ConfigProtos.Config.LoRaConfig.ModemPreset.UNRECOGNIZED) // Huh? didn't find it - keep same
newModemPreset = oldPrimary.loraConfig.modemPreset
// Generate a new AES256 key if the user changes channel name or the name is non-default and the settings changed
// if (newName != originalName || (newName.isNotEmpty() && modemConfig != oldPrimary.settings.modemConfig)) {
if (newName != originalName || newName.isNotEmpty()) {
if (newName != originalName || (newName.isNotEmpty() && newModemPreset != oldPrimary.loraConfig.modemPreset)) {
// Install a new customized channel
debug("ASSIGNING NEW AES256 KEY")
@ -325,9 +332,10 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
}
// No matter what apply the speed selection from the user
// newSettings.modemConfig = modemPreset -> TODO add from LoraConfig.ModemPreset?
val newLoRaConfig = ConfigProtos.Config.LoRaConfig.newBuilder()
.setModemPreset(newModemPreset)
installSettings(newSettings.build())
installSettings(newSettings.build(),newLoRaConfig.build())
}
}
.show()

Wyświetl plik

@ -8,10 +8,10 @@ class ChannelSetTest {
/** make sure we match the python and device code behavior */
@Test
fun matchPython() {
val url = Uri.parse("https://www.meshtastic.org/d/#CgUYAiIBAQ")
val url = Uri.parse("https://www.meshtastic.org/e/#CgUiAQEYAg")
val cs = ChannelSet(url)
// Assert.assertEquals("LongFast", cs.primaryChannel!!.name, )
// Assert.assertEquals("#LongFast-I", cs.primaryChannel!!.humanName, )
// Assert.assertEquals(url, cs.getChannelUrl(false))
Assert.assertEquals("LongFast", cs.primaryChannel!!.name)
Assert.assertEquals("#LongFast-I", cs.primaryChannel!!.humanName)
Assert.assertEquals(url, cs.getChannelUrl(false))
}
}