kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
add localonly & update apponly protobufs
rodzic
d7aa79edc6
commit
875939931e
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue