Merge pull request #439 from andrekir/localconfig

localonly & apponly protobuf updates
pull/440/head
Andre Kirchhoff 2022-05-30 17:59:17 -03:00 zatwierdzone przez GitHub
commit 1dd53b73e1
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
5 zmienionych plików z 107 dodań i 54 usunięć

Wyświetl plik

@ -1,16 +1,20 @@
package com.geeksville.mesh.model package com.geeksville.mesh.model
import com.geeksville.mesh.ChannelProtos import com.geeksville.mesh.ChannelProtos
import com.geeksville.mesh.ConfigProtos
import com.google.protobuf.ByteString import com.google.protobuf.ByteString
/** Utility function to make it easy to declare byte arrays - FIXME move someplace better */ /** 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 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(
data class Channel(val settings: ChannelProtos.ChannelSettings) { val settings: ChannelProtos.ChannelSettings,
val loraConfig: ConfigProtos.Config.LoRaConfig
) {
companion object { companion object {
// These bytes must match the well known and not secret bytes used the default channel AES128 key device code // 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, 0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59,
0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0x01 0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0x01
) )
@ -19,19 +23,24 @@ data class Channel(val settings: ChannelProtos.ChannelSettings) {
private val defaultPSK = private val defaultPSK =
byteArrayOfInts(1) // a shortstring code to indicate we need our default PSK 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( val default = Channel(
ChannelProtos.ChannelSettings.newBuilder() ChannelProtos.ChannelSettings.newBuilder()
// .setModemConfig(ChannelProtos.ChannelSettings.ModemConfig.LongFast)
.setPsk(ByteString.copyFrom(defaultPSK)) .setPsk(ByteString.copyFrom(defaultPSK))
.build(),
ConfigProtos.Config.LoRaConfig.newBuilder()
.setModemPreset(ConfigProtos.Config.LoRaConfig.ModemPreset.LongFast)
.build() .build()
) )
} }
/// Return the name of our channel as a human readable string. If empty string, assume "Default" per mesh.proto spec /// Return the name of our channel as a human readable string. If empty string, assume "Default" per mesh.proto spec
val name: String val name: String
get() = settings.name.ifEmpty { "Placeholder" /* get() = settings.name.ifEmpty {
when (settings.modemConfig) { // 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.ShortFast -> "ShortFast"
ConfigProtos.Config.LoRaConfig.ModemPreset.ShortSlow -> "ShortSlow" ConfigProtos.Config.LoRaConfig.ModemPreset.ShortSlow -> "ShortSlow"
ConfigProtos.Config.LoRaConfig.ModemPreset.MidFast -> "MidFast" 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.LongSlow -> "LongSlow"
ConfigProtos.Config.LoRaConfig.ModemPreset.VLongSlow -> "VLongSlow" ConfigProtos.Config.LoRaConfig.ModemPreset.VLongSlow -> "VLongSlow"
else -> "Invalid" else -> "Invalid"
}*/ }
} }
val psk: ByteString val psk: ByteString
@ -75,9 +84,13 @@ data class Channel(val settings: ChannelProtos.ChannelSettings) {
return "#${name}-${suffix}" return "#${name}-${suffix}"
} }
override fun equals(o: Any?): Boolean = (o is Channel) override fun equals(other: Any?): Boolean = (other is Channel)
&& psk.toByteArray() contentEquals o.psk.toByteArray() && psk.toByteArray() contentEquals other.psk.toByteArray()
&& name == o.name && 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 { ) : Logging {
companion object { 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 private const val base64Flags = Base64.URL_SAFE + Base64.NO_WRAP + Base64.NO_PADDING
@ -42,7 +42,7 @@ data class ChannelSet(
val primaryChannel: Channel? val primaryChannel: Channel?
get() = get() =
if (protobuf.settingsCount > 0) if (protobuf.settingsCount > 0)
Channel(protobuf.getSettings(0)) Channel(protobuf.getSettings(0), protobuf.loraConfig)
else else
null null
@ -54,10 +54,7 @@ data class ChannelSet(
val channelBytes = protobuf.toByteArray() ?: ByteArray(0) // if unset just use empty val channelBytes = protobuf.toByteArray() ?: ByteArray(0) // if unset just use empty
val enc = Base64.encodeToString(channelBytes, base64Flags) val enc = Base64.encodeToString(channelBytes, base64Flags)
val p = if (upperCasePrefix) val p = if (upperCasePrefix) prefix.uppercase() else prefix
prefix.uppercase()
else
prefix
return Uri.parse("$p$enc") return Uri.parse("$p$enc")
} }

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -8,10 +8,10 @@ class ChannelSetTest {
/** make sure we match the python and device code behavior */ /** make sure we match the python and device code behavior */
@Test @Test
fun matchPython() { 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) val cs = ChannelSet(url)
// Assert.assertEquals("LongFast", cs.primaryChannel!!.name, ) Assert.assertEquals("LongFast", cs.primaryChannel!!.name)
// Assert.assertEquals("#LongFast-I", cs.primaryChannel!!.humanName, ) Assert.assertEquals("#LongFast-I", cs.primaryChannel!!.humanName)
// Assert.assertEquals(url, cs.getChannelUrl(false)) Assert.assertEquals(url, cs.getChannelUrl(false))
} }
} }