Merge pull request #364 from meshtastic/2.1.14_working_changes

2.1.14 working changes
This commit is contained in:
Garth Vander Houwen 2023-06-24 09:20:31 -07:00 committed by GitHub
commit 7c2e71ef7e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 836 additions and 68 deletions

View file

@ -193,6 +193,7 @@
/* Begin PBXFileReference section */
A65FA974296876BF00A97686 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
C9697F9C279336B700250207 /* LocalMBTileOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalMBTileOverlay.swift; sourceTree = "<group>"; };
DD0E9C222A30CE3A00580CBB /* MeshtasticDataModelV14.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV14.xcdatamodel; sourceTree = "<group>"; };
DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminMessageList.swift; sourceTree = "<group>"; };
DD1925B628CDA5A400720036 /* CannedMessagesConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CannedMessagesConfigEnums.swift; sourceTree = "<group>"; };
DD1925B828CDA93900720036 /* SerialConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialConfigEnums.swift; sourceTree = "<group>"; };
@ -1306,7 +1307,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.1.11;
MARKETING_VERSION = 2.1.14;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTS_MACCATALYST = YES;
@ -1340,7 +1341,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.1.11;
MARKETING_VERSION = 2.1.14;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTS_MACCATALYST = YES;
@ -1587,6 +1588,7 @@
DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
DD0E9C222A30CE3A00580CBB /* MeshtasticDataModelV14.xcdatamodel */,
DDB75A1F2A10766D006ED576 /* MeshtasticDataModelV13.xcdatamodel */,
DDB759E12A04B264006ED576 /* MeshtasticDataModelV12.xcdatamodel */,
DDDEE5E229DBE43E00A8E078 /* MeshtasticDataModelV11.xcdatamodel */,
@ -1601,7 +1603,7 @@
DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */,
DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */,
);
currentVersion = DDB75A1F2A10766D006ED576 /* MeshtasticDataModelV13.xcdatamodel */;
currentVersion = DD0E9C222A30CE3A00580CBB /* MeshtasticDataModelV14.xcdatamodel */;
name = Meshtastic.xcdatamodeld;
path = Meshtastic/Meshtastic.xcdatamodeld;
sourceTree = "<group>";

View file

@ -423,7 +423,6 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
if myInfo != nil {
connectedPeripheral.num = myInfo!.myNodeNum
connectedPeripheral.firmwareVersion = myInfo?.firmwareVersion ?? "unknown".localized
connectedPeripheral.name = myInfo?.bleName ?? "unknown".localized
connectedPeripheral.longName = myInfo?.bleName ?? "unknown".localized
}
@ -445,12 +444,12 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
}
}
// Channels
if decodedInfo.channel.isInitialized {
if decodedInfo.channel.isInitialized && connectedPeripheral != nil {
nowKnown = true
channelPacket(channel: decodedInfo.channel, fromNum: connectedPeripheral.num, context: context!)
}
// Config
if decodedInfo.config.isInitialized && !invalidVersion {
if decodedInfo.config.isInitialized && !invalidVersion && connectedPeripheral != nil {
nowKnown = true
localConfig(config: decodedInfo.config, context: context!, nodeNum: self.connectedPeripheral.num, nodeLongName: self.connectedPeripheral.longName)
@ -472,6 +471,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
if decodedInfo.metadata.firmwareVersion.count > 0 && !invalidVersion {
nowKnown = true
deviceMetadataPacket(metadata: decodedInfo.metadata, fromNum: connectedPeripheral.num, context: context!)
connectedPeripheral.firmwareVersion = decodedInfo.metadata.firmwareVersion ?? "unknown".localized
}
// Log any other unknownApp calls
if !nowKnown { MeshLogger.log("🕸️ MESH PACKET received for Unknown App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")") }

View file

@ -89,17 +89,8 @@ func myInfoPacket (myInfo: MyNodeInfo, peripheralId: String, context: NSManagedO
let myInfoEntity = MyInfoEntity(context: context)
myInfoEntity.peripheralId = peripheralId
myInfoEntity.myNodeNum = Int64(myInfo.myNodeNum)
myInfoEntity.hasGps = myInfo.hasGps_p
myInfoEntity.hasWifi = myInfo.hasWifi_p
myInfoEntity.bitrate = myInfo.bitrate
// Swift does strings weird, this does work to get the version without the github hash
let lastDotIndex = myInfo.firmwareVersion.lastIndex(of: ".")
var version = myInfo.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset: 6, in: myInfo.firmwareVersion))]
version = version.dropLast()
myInfoEntity.firmwareVersion = String(version)
myInfoEntity.messageTimeoutMsec = Int32(bitPattern: myInfo.messageTimeoutMsec)
myInfoEntity.rebootCount = Int32(myInfo.rebootCount)
myInfoEntity.minAppVersion = Int32(bitPattern: myInfo.minAppVersion)
myInfoEntity.maxChannels = Int32(bitPattern: myInfo.maxChannels)
do {
try context.save()
print("💾 Saved a new myInfo for node number: \(String(myInfo.myNodeNum))")
@ -113,15 +104,8 @@ func myInfoPacket (myInfo: MyNodeInfo, peripheralId: String, context: NSManagedO
fetchedMyInfo[0].peripheralId = peripheralId
fetchedMyInfo[0].myNodeNum = Int64(myInfo.myNodeNum)
fetchedMyInfo[0].hasGps = myInfo.hasGps_p
fetchedMyInfo[0].bitrate = myInfo.bitrate
let lastDotIndex = myInfo.firmwareVersion.lastIndex(of: ".")// .lastIndex(of: ".", offsetBy: -1)
var version = myInfo.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset: 6, in: myInfo.firmwareVersion))]
version = version.dropLast()
fetchedMyInfo[0].firmwareVersion = String(version)
fetchedMyInfo[0].messageTimeoutMsec = Int32(bitPattern: myInfo.messageTimeoutMsec)
fetchedMyInfo[0].rebootCount = Int32(myInfo.rebootCount)
fetchedMyInfo[0].minAppVersion = Int32(bitPattern: myInfo.minAppVersion)
fetchedMyInfo[0].maxChannels = Int32(bitPattern: myInfo.maxChannels)
do {
try context.save()
@ -214,6 +198,11 @@ func deviceMetadataPacket (metadata: DeviceMetadata, fromNum: Int64, context: NS
newMetadata.hasEthernet = metadata.hasEthernet_p
newMetadata.role = Int32(metadata.role.rawValue)
newMetadata.positionFlags = Int32(metadata.positionFlags)
// Swift does strings weird, this does work to get the version without the github hash
let lastDotIndex = metadata.firmwareVersion.lastIndex(of: ".")
var version = metadata.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset: 6, in: metadata.firmwareVersion))]
version = version.dropLast()
newMetadata.firmwareVersion = String(version)
fetchedNode[0].metadata = newMetadata
do {
@ -272,7 +261,6 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
newUser.num = Int64(nodeInfo.num)
newUser.longName = nodeInfo.user.longName
newUser.shortName = nodeInfo.user.shortName
newUser.macaddr = nodeInfo.user.macaddr
newUser.hwModel = String(describing: nodeInfo.user.hwModel).uppercased()
newNode.user = newUser
}
@ -329,7 +317,6 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
fetchedNode[0].user!.num = Int64(nodeInfo.num)
fetchedNode[0].user!.longName = nodeInfo.user.longName
fetchedNode[0].user!.shortName = nodeInfo.user.shortName
fetchedNode[0].user!.macaddr = nodeInfo.user.macaddr
fetchedNode[0].user!.hwModel = String(describing: nodeInfo.user.hwModel).uppercased()
}

View file

@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>MeshtasticDataModelV13.xcdatamodel</string>
<string>MeshtasticDataModelV14.xcdatamodel</string>
</dict>
</plist>

View file

@ -0,0 +1,331 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21754" systemVersion="22F66" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="BluetoothConfigEntity" representedClassName="BluetoothConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="fixedPin" optional="YES" attributeType="Integer 32" defaultValueString="123456" usesScalarValueType="YES"/>
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="bluetoothConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="bluetoothConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="CannedMessageConfigEntity" representedClassName="CannedMessageConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="inputbrokerEventCcw" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="inputbrokerEventCw" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="inputbrokerEventPress" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="inputbrokerPinA" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="inputbrokerPinB" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
<attribute name="inputbrokerPinPress" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
<attribute name="messages" optional="YES" attributeType="String" minValueString="0" maxValueString="198"/>
<attribute name="rotary1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="sendBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="updown1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<relationship name="cannedMessagesConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="cannedMessageConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="ChannelEntity" representedClassName="ChannelEntity" syncable="YES" codeGenerationType="class">
<attribute name="downlinkEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="id" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="index" attributeType="Integer 32" minValueString="0" maxValueString="13" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="mute" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="name" optional="YES" attributeType="String"/>
<attribute name="psk" optional="YES" attributeType="Binary"/>
<attribute name="role" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="uplinkEnabled" attributeType="Boolean" usesScalarValueType="YES"/>
<relationship name="myInfoChannel" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="channels" inverseEntity="MyInfoEntity"/>
<fetchedProperty name="allPrivateMessages" optional="YES">
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="channel == $FETCH_SOURCE.index &amp;&amp; toUser == nil AND isEmoji == false"/>
</fetchedProperty>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="index"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="DeviceConfigEntity" representedClassName="DeviceConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="buttonGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="buzzerGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="debugLogEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="doubleTapAsButtonPress" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="isManaged" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="nodeInfoBroadcastSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="rebroadcastMode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="role" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="serialEnabled" optional="YES" attributeType="Boolean" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="deviceConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="deviceConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="DeviceMetadataEntity" representedClassName="DeviceMetadataEntity" syncable="YES" codeGenerationType="class">
<attribute name="canShutdown" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="deviceStateVersion" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="firmwareVersion" optional="YES" attributeType="String"/>
<attribute name="hasBluetooth" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="hasEthernet" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="hasWifi" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="hwModel" optional="YES" attributeType="String"/>
<attribute name="positionFlags" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="role" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="metadataNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="metadata" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="DisplayConfigEntity" representedClassName="DisplayConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="compassNorthTop" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="displayMode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="flipScreen" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="gpsFormat" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="headingBold" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<attribute name="oledType" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="screenCarouselInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="screenOnSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="wakeOnTapOrMotion" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<relationship name="displayConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="displayConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="ExternalNotificationConfigEntity" representedClassName="ExternalNotificationConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="active" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="alertBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="alertBellBuzzer" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="alertBellVibra" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="alertMessage" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="alertMessageBuzzer" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="alertMessageVibra" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="nagTimeout" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="output" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="outputBuzzer" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="outputMilliseconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="outputVibra" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="usePWM" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<relationship name="externalNotificationConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="externalNotificationConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="LocationEntity" representedClassName="LocationEntity" syncable="YES" codeGenerationType="class">
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="heading" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="id" attributeType="Integer 32" usesScalarValueType="YES"/>
<attribute name="latitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="longitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="speed" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="routeLocation" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RouteEntity" inverseName="locations" inverseEntity="RouteEntity"/>
</entity>
<entity name="LoRaConfigEntity" representedClassName="LoRaConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="bandwidth" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="channelNum" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="codingRate" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="frequencyOffset" optional="YES" attributeType="Float" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="hopLimit" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="modemPreset" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="overrideDutyCycle" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="overrideFrequency" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="regionCode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="spreadFactor" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="sx126xRxBoostedGain" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="txEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<attribute name="txPower" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="usePreset" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<relationship name="loRaConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="loRaConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="MessageEntity" representedClassName="MessageEntity" syncable="YES" codeGenerationType="class">
<attribute name="ackError" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="ackSNR" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="ackTimestamp" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="admin" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="adminDescription" optional="YES" attributeType="String"/>
<attribute name="channel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="isEmoji" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="messageId" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="messagePayload" optional="YES" attributeType="String" defaultValueString=""/>
<attribute name="messagePayloadMarkdown" optional="YES" attributeType="String"/>
<attribute name="messageTimestamp" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="realACK" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="receivedACK" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="replyID" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<relationship name="fromUser" optional="YES" maxCount="1" deletionRule="Nullify" ordered="YES" destinationEntity="UserEntity" inverseName="sentMessages" inverseEntity="UserEntity"/>
<relationship name="toUser" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="receivedMessages" inverseEntity="UserEntity"/>
<fetchedProperty name="tapbacks" optional="YES">
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="replyID == $FETCH_SOURCE.messageId AND isEmoji == true"/>
</fetchedProperty>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="messageId"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="MQTTConfigEntity" representedClassName="MQTTConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="address" optional="YES" attributeType="String" maxValueString="30"/>
<attribute name="enabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="encryptionEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="jsonEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="password" optional="YES" attributeType="String" maxValueString="30"/>
<attribute name="root" optional="YES" attributeType="String" defaultValueString="msh"/>
<attribute name="tlsEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="username" optional="YES" attributeType="String" maxValueString="30"/>
<relationship name="mqttConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="mqttConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="MyInfoEntity" representedClassName="MyInfoEntity" syncable="YES" codeGenerationType="class">
<attribute name="adminIndex" optional="YES" attributeType="Integer 32" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="bleName" optional="YES" attributeType="String"/>
<attribute name="minAppVersion" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="myNodeNum" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="peripheralId" optional="YES" attributeType="String"/>
<attribute name="rebootCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="channels" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="ChannelEntity" inverseName="myInfoChannel" inverseEntity="ChannelEntity"/>
<relationship name="myInfoNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="myInfo" inverseEntity="NodeInfoEntity"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="myNodeNum"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="NetworkConfigEntity" representedClassName="NetworkConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="dns" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="ethEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="gateway" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="ip" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="ntpServer" optional="YES" attributeType="String"/>
<attribute name="subnet" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="wifiEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="wifiMode" optional="YES" attributeType="Integer 32" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="wifiPsk" optional="YES" attributeType="String" minValueString="0" maxValueString="60"/>
<attribute name="wifiSsid" optional="YES" attributeType="String" minValueString="0" maxValueString="30"/>
<relationship name="networkConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="networkConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="NodeInfoEntity" representedClassName="NodeInfoEntity" syncable="YES" codeGenerationType="class">
<attribute name="bleName" optional="YES" attributeType="String"/>
<attribute name="channel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="lastHeard" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="peripheralId" optional="YES" attributeType="String"/>
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<relationship name="bluetoothConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="BluetoothConfigEntity" inverseName="bluetoothConfigNode" inverseEntity="BluetoothConfigEntity"/>
<relationship name="cannedMessageConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="CannedMessageConfigEntity" inverseName="cannedMessagesConfigNode" inverseEntity="CannedMessageConfigEntity"/>
<relationship name="deviceConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DeviceConfigEntity" inverseName="deviceConfigNode" inverseEntity="DeviceConfigEntity"/>
<relationship name="displayConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DisplayConfigEntity" inverseName="displayConfigNode" inverseEntity="DisplayConfigEntity"/>
<relationship name="externalNotificationConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="ExternalNotificationConfigEntity" inverseName="externalNotificationConfigNode" inverseEntity="ExternalNotificationConfigEntity"/>
<relationship name="loRaConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="LoRaConfigEntity" inverseName="loRaConfigNode" inverseEntity="LoRaConfigEntity"/>
<relationship name="metadata" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DeviceMetadataEntity" inverseName="metadataNode" inverseEntity="DeviceMetadataEntity"/>
<relationship name="mqttConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MQTTConfigEntity" inverseName="mqttConfigNode" inverseEntity="MQTTConfigEntity"/>
<relationship name="myInfo" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="myInfoNode" inverseEntity="MyInfoEntity"/>
<relationship name="networkConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NetworkConfigEntity" inverseName="networkConfigNode" inverseEntity="NetworkConfigEntity"/>
<relationship name="positionConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PositionConfigEntity" inverseName="positionConfigNode" inverseEntity="PositionConfigEntity"/>
<relationship name="positions" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PositionEntity" inverseName="nodePosition" inverseEntity="PositionEntity"/>
<relationship name="rangeTestConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RangeTestConfigEntity" inverseName="rangeTestConfigNode" inverseEntity="RangeTestConfigEntity"/>
<relationship name="rtttlConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RTTTLConfigEntity" inverseName="rtttlConfigNode" inverseEntity="RTTTLConfigEntity"/>
<relationship name="serialConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SerialConfigEntity" inverseName="serialConfigNode" inverseEntity="SerialConfigEntity"/>
<relationship name="telemetries" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TelemetryEntity" inverseName="nodeTelemetry" inverseEntity="TelemetryEntity"/>
<relationship name="telemetryConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TelemetryConfigEntity" inverseName="telemetryConfigNode" inverseEntity="TelemetryConfigEntity"/>
<relationship name="user" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="userNode" inverseEntity="UserEntity"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="num"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="PositionConfigEntity" representedClassName="PositionConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="broadcastSmartMinimumDistance" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="broadcastSmartMinimumIntervalSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="deviceGpsEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="fixedPosition" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="gpsAttemptTime" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="gpsUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="positionBroadcastSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="positionFlags" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="rxGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="smartPositionEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="txGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="positionConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positionConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="PositionEntity" representedClassName="PositionEntity" syncable="YES" codeGenerationType="class">
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="heading" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="latest" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="latitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="longitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="satsInView" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="seqNo" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="speed" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<relationship name="nodePosition" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positions" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="RangeTestConfigEntity" representedClassName="RangeTestConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="save" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="sender" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
<relationship name="rangeTestConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="rangeTestConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="RouteEntity" representedClassName="RouteEntity" syncable="YES" codeGenerationType="class">
<attribute name="color" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="id" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="name" optional="YES" attributeType="String"/>
<attribute name="notes" optional="YES" attributeType="String"/>
<relationship name="locations" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="LocationEntity" inverseName="routeLocation" inverseEntity="LocationEntity"/>
</entity>
<entity name="RTTTLConfigEntity" representedClassName="RTTTLConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="ringtone" optional="YES" attributeType="String" maxValueString="228" defaultValueString=""/>
<relationship name="rtttlConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="rtttlConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="SerialConfigEntity" representedClassName="SerialConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="baudRate" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="echo" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="rxd" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="timeout" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="txd" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="serialConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="serialConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="TelemetryConfigEntity" representedClassName="TelemetryConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="deviceUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="environmentDisplayFahrenheit" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="environmentMeasurementEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="environmentScreenEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="environmentUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="telemetryConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetryConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="TelemetryEntity" representedClassName="TelemetryEntity" syncable="YES" codeGenerationType="class">
<attribute name="airUtilTx" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="barometricPressure" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="batteryLevel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="channelUtilization" optional="YES" attributeType="Float" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="current" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="gasResistance" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="metricsType" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="relativeHumidity" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="temperature" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="voltage" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<relationship name="nodeTelemetry" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetries" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="UserEntity" representedClassName="UserEntity" syncable="YES" codeGenerationType="class">
<attribute name="hwModel" attributeType="String"/>
<attribute name="isLicensed" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="longName" attributeType="String"/>
<attribute name="mute" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="shortName" attributeType="String"/>
<attribute name="userId" attributeType="String"/>
<relationship name="receivedMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="toUser" inverseEntity="MessageEntity"/>
<relationship name="sentMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="fromUser" inverseEntity="MessageEntity"/>
<relationship name="userNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="user" inverseEntity="NodeInfoEntity"/>
<fetchedProperty name="adminMessages" optional="YES">
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="(fromUser.num == $FETCH_SOURCE.num) AND isEmoji == false AND admin = true"/>
</fetchedProperty>
<fetchedProperty name="allMessages" optional="YES">
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="((toUser.num == $FETCH_SOURCE.num) OR (fromUser.num == $FETCH_SOURCE.num)) AND toUser != nil AND fromUser != nil AND isEmoji == false AND admin = false"/>
</fetchedProperty>
</entity>
<entity name="WaypointEntity" representedClassName="WaypointEntity" syncable="YES" codeGenerationType="class">
<attribute name="created" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="expire" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="icon" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="lastUpdated" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="latitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="locked" attributeType="Integer 64" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="longDescription" optional="YES" attributeType="String" maxValueString="100"/>
<attribute name="longitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="name" attributeType="String" minValueString="1" maxValueString="30"/>
</entity>
</model>

View file

@ -129,7 +129,6 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
newUser.num = Int64(packet.from)
newUser.longName = newUserMessage.longName
newUser.shortName = newUserMessage.shortName
newUser.macaddr = newUserMessage.macaddr
newUser.hwModel = String(describing: newUserMessage.hwModel).uppercased()
newNode.user = newUser
}
@ -159,7 +158,6 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
fetchedNode[0].user!.num = Int64(nodeInfoMessage.num)
fetchedNode[0].user!.longName = nodeInfoMessage.user.longName
fetchedNode[0].user!.shortName = nodeInfoMessage.user.shortName
fetchedNode[0].user!.macaddr = nodeInfoMessage.user.macaddr
fetchedNode[0].user!.hwModel = String(describing: nodeInfoMessage.user.hwModel).uppercased()
}
}

View file

@ -533,6 +533,10 @@ struct Config {
/// 0 for default of 10 seconds
var minWakeSecs: UInt32 = 0
///
/// I2C address of INA_2XX to use for reading device battery voltage
var deviceBatteryInaAddress: UInt32 = 0
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
@ -1805,6 +1809,7 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
6: .standard(proto: "sds_secs"),
7: .standard(proto: "ls_secs"),
8: .standard(proto: "min_wake_secs"),
9: .standard(proto: "device_battery_ina_address"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -1821,6 +1826,7 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
case 6: try { try decoder.decodeSingularUInt32Field(value: &self.sdsSecs) }()
case 7: try { try decoder.decodeSingularUInt32Field(value: &self.lsSecs) }()
case 8: try { try decoder.decodeSingularUInt32Field(value: &self.minWakeSecs) }()
case 9: try { try decoder.decodeSingularUInt32Field(value: &self.deviceBatteryInaAddress) }()
default: break
}
}
@ -1851,6 +1857,9 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
if self.minWakeSecs != 0 {
try visitor.visitSingularUInt32Field(value: self.minWakeSecs, fieldNumber: 8)
}
if self.deviceBatteryInaAddress != 0 {
try visitor.visitSingularUInt32Field(value: self.deviceBatteryInaAddress, fieldNumber: 9)
}
try unknownFields.traverse(visitor: &visitor)
}
@ -1863,6 +1872,7 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
if lhs.sdsSecs != rhs.sdsSecs {return false}
if lhs.lsSecs != rhs.lsSecs {return false}
if lhs.minWakeSecs != rhs.minWakeSecs {return false}
if lhs.deviceBatteryInaAddress != rhs.deviceBatteryInaAddress {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}

View file

@ -109,7 +109,8 @@ struct DeviceState {
mutating func clearOwner() {_uniqueStorage()._owner = nil}
///
/// TODO: REPLACE
/// Deprecated in 2.1.x
/// Old node_db. See NodeInfoLite node_db_lite
var nodeDb: [NodeInfo] {
get {return _storage._nodeDb}
set {_uniqueStorage()._nodeDb = newValue}
@ -179,6 +180,13 @@ struct DeviceState {
set {_uniqueStorage()._nodeRemoteHardwarePins = newValue}
}
///
/// New lite version of NodeDB to decrease
var nodeDbLite: [NodeInfoLite] {
get {return _storage._nodeDbLite}
set {_uniqueStorage()._nodeDbLite = newValue}
}
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
@ -186,6 +194,118 @@ struct DeviceState {
fileprivate var _storage = _StorageClass.defaultInstance
}
struct NodeInfoLite {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
///
/// The node number
var num: UInt32 {
get {return _storage._num}
set {_uniqueStorage()._num = newValue}
}
///
/// The user info for this node
var user: User {
get {return _storage._user ?? User()}
set {_uniqueStorage()._user = newValue}
}
/// Returns true if `user` has been explicitly set.
var hasUser: Bool {return _storage._user != nil}
/// Clears the value of `user`. Subsequent reads from it will return its default value.
mutating func clearUser() {_uniqueStorage()._user = nil}
///
/// This position data. Note: before 1.2.14 we would also store the last time we've heard from this node in position.time, that is no longer true.
/// Position.time now indicates the last time we received a POSITION from that node.
var position: PositionLite {
get {return _storage._position ?? PositionLite()}
set {_uniqueStorage()._position = newValue}
}
/// Returns true if `position` has been explicitly set.
var hasPosition: Bool {return _storage._position != nil}
/// Clears the value of `position`. Subsequent reads from it will return its default value.
mutating func clearPosition() {_uniqueStorage()._position = nil}
///
/// Returns the Signal-to-noise ratio (SNR) of the last received message,
/// as measured by the receiver. Return SNR of the last received message in dB
var snr: Float {
get {return _storage._snr}
set {_uniqueStorage()._snr = newValue}
}
///
/// Set to indicate the last time we received a packet from this node
var lastHeard: UInt32 {
get {return _storage._lastHeard}
set {_uniqueStorage()._lastHeard = newValue}
}
///
/// The latest device metrics for the node.
var deviceMetrics: DeviceMetrics {
get {return _storage._deviceMetrics ?? DeviceMetrics()}
set {_uniqueStorage()._deviceMetrics = newValue}
}
/// Returns true if `deviceMetrics` has been explicitly set.
var hasDeviceMetrics: Bool {return _storage._deviceMetrics != nil}
/// Clears the value of `deviceMetrics`. Subsequent reads from it will return its default value.
mutating func clearDeviceMetrics() {_uniqueStorage()._deviceMetrics = nil}
///
/// local channel index we heard that node on. Only populated if its not the default channel.
var channel: UInt32 {
get {return _storage._channel}
set {_uniqueStorage()._channel = newValue}
}
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
fileprivate var _storage = _StorageClass.defaultInstance
}
///
/// Position with static location information only for NodeDBLite
struct PositionLite {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
///
/// The new preferred location encoding, multiply by 1e-7 to get degrees
/// in floating point
var latitudeI: Int32 = 0
///
/// TODO: REPLACE
var longitudeI: Int32 = 0
///
/// In meters above MSL (but see issue #359)
var altitude: Int32 = 0
///
/// This is usually not sent over the mesh (to save space), but it is sent
/// from the phone so that the local device can set its RTC If it is sent over
/// the mesh (because there are devices on the mesh without GPS), it will only
/// be sent by devices which has a hardware GPS clock.
/// seconds since 1970
var time: UInt32 = 0
///
/// TODO: REPLACE
var locationSource: Position.LocSource = .locUnset
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
///
/// The on-disk saved channels
struct ChannelFile {
@ -302,6 +422,8 @@ struct NodeRemoteHardwarePin {
#if swift(>=5.5) && canImport(_Concurrency)
extension ScreenFonts: @unchecked Sendable {}
extension DeviceState: @unchecked Sendable {}
extension NodeInfoLite: @unchecked Sendable {}
extension PositionLite: @unchecked Sendable {}
extension ChannelFile: @unchecked Sendable {}
extension OEMStore: @unchecked Sendable {}
extension NodeRemoteHardwarePin: @unchecked Sendable {}
@ -332,6 +454,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
11: .standard(proto: "did_gps_reset"),
12: .standard(proto: "rx_waypoint"),
13: .standard(proto: "node_remote_hardware_pins"),
14: .standard(proto: "node_db_lite"),
]
fileprivate class _StorageClass {
@ -345,6 +468,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
var _didGpsReset: Bool = false
var _rxWaypoint: MeshPacket? = nil
var _nodeRemoteHardwarePins: [NodeRemoteHardwarePin] = []
var _nodeDbLite: [NodeInfoLite] = []
static let defaultInstance = _StorageClass()
@ -361,6 +485,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
_didGpsReset = source._didGpsReset
_rxWaypoint = source._rxWaypoint
_nodeRemoteHardwarePins = source._nodeRemoteHardwarePins
_nodeDbLite = source._nodeDbLite
}
}
@ -389,6 +514,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
case 11: try { try decoder.decodeSingularBoolField(value: &_storage._didGpsReset) }()
case 12: try { try decoder.decodeSingularMessageField(value: &_storage._rxWaypoint) }()
case 13: try { try decoder.decodeRepeatedMessageField(value: &_storage._nodeRemoteHardwarePins) }()
case 14: try { try decoder.decodeRepeatedMessageField(value: &_storage._nodeDbLite) }()
default: break
}
}
@ -431,6 +557,9 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
if !_storage._nodeRemoteHardwarePins.isEmpty {
try visitor.visitRepeatedMessageField(value: _storage._nodeRemoteHardwarePins, fieldNumber: 13)
}
if !_storage._nodeDbLite.isEmpty {
try visitor.visitRepeatedMessageField(value: _storage._nodeDbLite, fieldNumber: 14)
}
}
try unknownFields.traverse(visitor: &visitor)
}
@ -450,6 +579,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
if _storage._didGpsReset != rhs_storage._didGpsReset {return false}
if _storage._rxWaypoint != rhs_storage._rxWaypoint {return false}
if _storage._nodeRemoteHardwarePins != rhs_storage._nodeRemoteHardwarePins {return false}
if _storage._nodeDbLite != rhs_storage._nodeDbLite {return false}
return true
}
if !storagesAreEqual {return false}
@ -459,6 +589,178 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
}
}
extension NodeInfoLite: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".NodeInfoLite"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "num"),
2: .same(proto: "user"),
3: .same(proto: "position"),
4: .same(proto: "snr"),
5: .standard(proto: "last_heard"),
6: .standard(proto: "device_metrics"),
7: .same(proto: "channel"),
]
fileprivate class _StorageClass {
var _num: UInt32 = 0
var _user: User? = nil
var _position: PositionLite? = nil
var _snr: Float = 0
var _lastHeard: UInt32 = 0
var _deviceMetrics: DeviceMetrics? = nil
var _channel: UInt32 = 0
static let defaultInstance = _StorageClass()
private init() {}
init(copying source: _StorageClass) {
_num = source._num
_user = source._user
_position = source._position
_snr = source._snr
_lastHeard = source._lastHeard
_deviceMetrics = source._deviceMetrics
_channel = source._channel
}
}
fileprivate mutating func _uniqueStorage() -> _StorageClass {
if !isKnownUniquelyReferenced(&_storage) {
_storage = _StorageClass(copying: _storage)
}
return _storage
}
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
_ = _uniqueStorage()
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularUInt32Field(value: &_storage._num) }()
case 2: try { try decoder.decodeSingularMessageField(value: &_storage._user) }()
case 3: try { try decoder.decodeSingularMessageField(value: &_storage._position) }()
case 4: try { try decoder.decodeSingularFloatField(value: &_storage._snr) }()
case 5: try { try decoder.decodeSingularFixed32Field(value: &_storage._lastHeard) }()
case 6: try { try decoder.decodeSingularMessageField(value: &_storage._deviceMetrics) }()
case 7: try { try decoder.decodeSingularUInt32Field(value: &_storage._channel) }()
default: break
}
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
if _storage._num != 0 {
try visitor.visitSingularUInt32Field(value: _storage._num, fieldNumber: 1)
}
try { if let v = _storage._user {
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
} }()
try { if let v = _storage._position {
try visitor.visitSingularMessageField(value: v, fieldNumber: 3)
} }()
if _storage._snr != 0 {
try visitor.visitSingularFloatField(value: _storage._snr, fieldNumber: 4)
}
if _storage._lastHeard != 0 {
try visitor.visitSingularFixed32Field(value: _storage._lastHeard, fieldNumber: 5)
}
try { if let v = _storage._deviceMetrics {
try visitor.visitSingularMessageField(value: v, fieldNumber: 6)
} }()
if _storage._channel != 0 {
try visitor.visitSingularUInt32Field(value: _storage._channel, fieldNumber: 7)
}
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: NodeInfoLite, rhs: NodeInfoLite) -> Bool {
if lhs._storage !== rhs._storage {
let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in
let _storage = _args.0
let rhs_storage = _args.1
if _storage._num != rhs_storage._num {return false}
if _storage._user != rhs_storage._user {return false}
if _storage._position != rhs_storage._position {return false}
if _storage._snr != rhs_storage._snr {return false}
if _storage._lastHeard != rhs_storage._lastHeard {return false}
if _storage._deviceMetrics != rhs_storage._deviceMetrics {return false}
if _storage._channel != rhs_storage._channel {return false}
return true
}
if !storagesAreEqual {return false}
}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension PositionLite: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".PositionLite"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .standard(proto: "latitude_i"),
2: .standard(proto: "longitude_i"),
3: .same(proto: "altitude"),
4: .same(proto: "time"),
5: .standard(proto: "location_source"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularSFixed32Field(value: &self.latitudeI) }()
case 2: try { try decoder.decodeSingularSFixed32Field(value: &self.longitudeI) }()
case 3: try { try decoder.decodeSingularInt32Field(value: &self.altitude) }()
case 4: try { try decoder.decodeSingularFixed32Field(value: &self.time) }()
case 5: try { try decoder.decodeSingularEnumField(value: &self.locationSource) }()
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.latitudeI != 0 {
try visitor.visitSingularSFixed32Field(value: self.latitudeI, fieldNumber: 1)
}
if self.longitudeI != 0 {
try visitor.visitSingularSFixed32Field(value: self.longitudeI, fieldNumber: 2)
}
if self.altitude != 0 {
try visitor.visitSingularInt32Field(value: self.altitude, fieldNumber: 3)
}
if self.time != 0 {
try visitor.visitSingularFixed32Field(value: self.time, fieldNumber: 4)
}
if self.locationSource != .locUnset {
try visitor.visitSingularEnumField(value: self.locationSource, fieldNumber: 5)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: PositionLite, rhs: PositionLite) -> Bool {
if lhs.latitudeI != rhs.latitudeI {return false}
if lhs.longitudeI != rhs.longitudeI {return false}
if lhs.altitude != rhs.altitude {return false}
if lhs.time != rhs.time {return false}
if lhs.locationSource != rhs.locationSource {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension ChannelFile: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".ChannelFile"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [

View file

@ -222,6 +222,17 @@ struct LocalModuleConfig {
/// Clears the value of `remoteHardware`. Subsequent reads from it will return its default value.
mutating func clearRemoteHardware() {_uniqueStorage()._remoteHardware = nil}
///
/// The part of the config that is specific to the Neighbor Info module
var neighborInfo: ModuleConfig.NeighborInfoConfig {
get {return _storage._neighborInfo ?? ModuleConfig.NeighborInfoConfig()}
set {_uniqueStorage()._neighborInfo = newValue}
}
/// Returns true if `neighborInfo` has been explicitly set.
var hasNeighborInfo: Bool {return _storage._neighborInfo != nil}
/// Clears the value of `neighborInfo`. Subsequent reads from it will return its default value.
mutating func clearNeighborInfo() {_uniqueStorage()._neighborInfo = nil}
///
/// A version integer used to invalidate old save files when we make
/// incompatible changes This integer is set at build time and is private to
@ -383,6 +394,7 @@ extension LocalModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
7: .standard(proto: "canned_message"),
9: .same(proto: "audio"),
10: .standard(proto: "remote_hardware"),
11: .standard(proto: "neighbor_info"),
8: .same(proto: "version"),
]
@ -396,6 +408,7 @@ extension LocalModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
var _cannedMessage: ModuleConfig.CannedMessageConfig? = nil
var _audio: ModuleConfig.AudioConfig? = nil
var _remoteHardware: ModuleConfig.RemoteHardwareConfig? = nil
var _neighborInfo: ModuleConfig.NeighborInfoConfig? = nil
var _version: UInt32 = 0
static let defaultInstance = _StorageClass()
@ -412,6 +425,7 @@ extension LocalModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
_cannedMessage = source._cannedMessage
_audio = source._audio
_remoteHardware = source._remoteHardware
_neighborInfo = source._neighborInfo
_version = source._version
}
}
@ -441,6 +455,7 @@ extension LocalModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
case 8: try { try decoder.decodeSingularUInt32Field(value: &_storage._version) }()
case 9: try { try decoder.decodeSingularMessageField(value: &_storage._audio) }()
case 10: try { try decoder.decodeSingularMessageField(value: &_storage._remoteHardware) }()
case 11: try { try decoder.decodeSingularMessageField(value: &_storage._neighborInfo) }()
default: break
}
}
@ -483,6 +498,9 @@ extension LocalModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
try { if let v = _storage._remoteHardware {
try visitor.visitSingularMessageField(value: v, fieldNumber: 10)
} }()
try { if let v = _storage._neighborInfo {
try visitor.visitSingularMessageField(value: v, fieldNumber: 11)
} }()
}
try unknownFields.traverse(visitor: &visitor)
}
@ -501,6 +519,7 @@ extension LocalModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
if _storage._cannedMessage != rhs_storage._cannedMessage {return false}
if _storage._audio != rhs_storage._audio {return false}
if _storage._remoteHardware != rhs_storage._remoteHardware {return false}
if _storage._neighborInfo != rhs_storage._neighborInfo {return false}
if _storage._version != rhs_storage._version {return false}
return true
}

View file

@ -106,6 +106,10 @@ enum HardwareModel: SwiftProtobuf.Enum {
/// B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station
case stationG1 // = 25
///
/// RAK11310 (RP2040 + SX1262)
case rak11310 // = 26
///
/// ---------------------------------------------------------------------------
/// Less common/prototype boards listed here (needs one more byte over the air)
@ -168,6 +172,10 @@ enum HardwareModel: SwiftProtobuf.Enum {
/// BetaFPV ExpressLRS "Nano" TX Module 900MHz with ESP32 CPU
case betafpv900NanoTx // = 46
///
/// Raspberry Pi Pico (W) with Waveshare SX1262 LoRa Node Module
case rpiPico // = 47
///
/// ------------------------------------------------------------------------------------------------------------------------------------------
/// Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
@ -200,6 +208,7 @@ enum HardwareModel: SwiftProtobuf.Enum {
case 16: self = .tloraT3S3
case 17: self = .nanoG1Explorer
case 25: self = .stationG1
case 26: self = .rak11310
case 32: self = .loraRelayV1
case 33: self = .nrf52840Dk
case 34: self = .ppr
@ -215,6 +224,7 @@ enum HardwareModel: SwiftProtobuf.Enum {
case 44: self = .heltecWslV3
case 45: self = .betafpv2400Tx
case 46: self = .betafpv900NanoTx
case 47: self = .rpiPico
case 255: self = .privateHw
default: self = .UNRECOGNIZED(rawValue)
}
@ -241,6 +251,7 @@ enum HardwareModel: SwiftProtobuf.Enum {
case .tloraT3S3: return 16
case .nanoG1Explorer: return 17
case .stationG1: return 25
case .rak11310: return 26
case .loraRelayV1: return 32
case .nrf52840Dk: return 33
case .ppr: return 34
@ -256,6 +267,7 @@ enum HardwareModel: SwiftProtobuf.Enum {
case .heltecWslV3: return 44
case .betafpv2400Tx: return 45
case .betafpv900NanoTx: return 46
case .rpiPico: return 47
case .privateHw: return 255
case .UNRECOGNIZED(let i): return i
}
@ -287,6 +299,7 @@ extension HardwareModel: CaseIterable {
.tloraT3S3,
.nanoG1Explorer,
.stationG1,
.rak11310,
.loraRelayV1,
.nrf52840Dk,
.ppr,
@ -302,6 +315,7 @@ extension HardwareModel: CaseIterable {
.heltecWslV3,
.betafpv2400Tx,
.betafpv900NanoTx,
.rpiPico,
.privateHw,
]
}
@ -841,6 +855,7 @@ struct User {
var shortName: String = String()
///
/// Deprecated in Meshtastic 2.1.x
/// This is the addr of the radio.
/// Not populated by the phone, but added by the esp32 when broadcasting
var macaddr: Data = Data()
@ -1615,15 +1630,18 @@ struct MyNodeInfo {
var myNodeNum: UInt32 = 0
///
/// Deprecated in 2.1.x (Source from device_metadata)
/// Note: This flag merely means we detected a hardware GPS in our node.
/// Not the same as UserPreferences.location_sharing
var hasGps_p: Bool = false
///
/// Deprecated in 2.1.x
/// The maximum number of 'software' channels that can be set on this node.
var maxChannels: UInt32 = 0
///
/// Deprecated in 2.1.x (Source from device_metadata)
/// 0.0.5 etc...
var firmwareVersion: String = String()
@ -1651,10 +1669,12 @@ struct MyNodeInfo {
var rebootCount: UInt32 = 0
///
/// Deprecated in 2.1.x
/// Calculated bitrate of the current channel (in Bytes Per Second)
var bitrate: Float = 0
///
/// Deprecated in 2.1.x
/// How long before we consider a message abandoned and we can clear our
/// caches of any messages in flight Normally quite large to handle the worst case
/// message delivery time, 5 minutes.
@ -1667,22 +1687,27 @@ struct MyNodeInfo {
var minAppVersion: UInt32 = 0
///
/// Deprecated in 2.1.x (Only used on device to keep track of utilization)
/// 24 time windows of 1hr each with the airtime transmitted out of the device per hour.
var airPeriodTx: [UInt32] = []
///
/// Deprecated in 2.1.x (Only used on device to keep track of utilization)
/// 24 time windows of 1hr each with the airtime of valid packets for your mesh.
var airPeriodRx: [UInt32] = []
///
/// Deprecated in 2.1.x (Source from DeviceMetadata instead)
/// Is the device wifi capable?
var hasWifi_p: Bool = false
///
/// Deprecated in 2.1.x (Source from DeviceMetrics telemetry payloads)
/// Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise).
var channelUtilization: Float = 0
///
/// Deprecated in 2.1.x (Source from DeviceMetrics telemetry payloads)
/// Percent of airtime for transmission used within the last hour.
var airUtilTx: Float = 0
@ -2385,6 +2410,7 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding {
16: .same(proto: "TLORA_T3_S3"),
17: .same(proto: "NANO_G1_EXPLORER"),
25: .same(proto: "STATION_G1"),
26: .same(proto: "RAK11310"),
32: .same(proto: "LORA_RELAY_V1"),
33: .same(proto: "NRF52840DK"),
34: .same(proto: "PPR"),
@ -2400,6 +2426,7 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding {
44: .same(proto: "HELTEC_WSL_V3"),
45: .same(proto: "BETAFPV_2400_TX"),
46: .same(proto: "BETAFPV_900_NANO_TX"),
47: .same(proto: "RPI_PICO"),
255: .same(proto: "PRIVATE_HW"),
]
}

View file

@ -174,6 +174,16 @@ struct ModuleConfig {
set {payloadVariant = .remoteHardware(newValue)}
}
///
/// TODO: REPLACE
var neighborInfo: ModuleConfig.NeighborInfoConfig {
get {
if case .neighborInfo(let v)? = payloadVariant {return v}
return ModuleConfig.NeighborInfoConfig()
}
set {payloadVariant = .neighborInfo(newValue)}
}
var unknownFields = SwiftProtobuf.UnknownStorage()
///
@ -206,6 +216,9 @@ struct ModuleConfig {
///
/// TODO: REPLACE
case remoteHardware(ModuleConfig.RemoteHardwareConfig)
///
/// TODO: REPLACE
case neighborInfo(ModuleConfig.NeighborInfoConfig)
#if !swift(>=4.1)
static func ==(lhs: ModuleConfig.OneOf_PayloadVariant, rhs: ModuleConfig.OneOf_PayloadVariant) -> Bool {
@ -249,6 +262,10 @@ struct ModuleConfig {
guard case .remoteHardware(let l) = lhs, case .remoteHardware(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.neighborInfo, .neighborInfo): return {
guard case .neighborInfo(let l) = lhs, case .neighborInfo(let r) = rhs else { preconditionFailure() }
return l == r
}()
default: return false
}
}
@ -333,6 +350,27 @@ struct ModuleConfig {
init() {}
}
///
/// NeighborInfoModule Config
struct NeighborInfoConfig {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
///
/// Whether the Module is enabled
var enabled: Bool = false
///
/// Interval in seconds of how often we should try to send our
/// Neighbor Info to the mesh
var updateInterval: UInt32 = 0
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
///
/// Audio Config for codec2 voice
struct AudioConfig {
@ -433,7 +471,6 @@ struct ModuleConfig {
///
/// Preferences for the SerialModule
/// FIXME - Move this out of UserPreferences and into a section for module configuration.
var enabled: Bool = false
///
@ -441,15 +478,15 @@ struct ModuleConfig {
var echo: Bool = false
///
/// TODO: REPLACE
/// RX pin (should match Arduino gpio pin number)
var rxd: UInt32 = 0
///
/// TODO: REPLACE
/// TX pin (should match Arduino gpio pin number)
var txd: UInt32 = 0
///
/// TODO: REPLACE
/// Serial baud rate
var baud: ModuleConfig.SerialConfig.Serial_Baud = .baudDefault
///
@ -457,9 +494,15 @@ struct ModuleConfig {
var timeout: UInt32 = 0
///
/// TODO: REPLACE
/// Mode for serial module operation
var mode: ModuleConfig.SerialConfig.Serial_Mode = .default
///
/// Overrides the platform's defacto Serial port instance to use with Serial module config settings
/// This is currently only usable in output modes like NMEA / CalTopo and may behave strangely or not work at all in other modes
/// Existing logging over the Serial Console will still be present
var overrideConsoleSerialPort: Bool = false
var unknownFields = SwiftProtobuf.UnknownStorage()
///
@ -543,6 +586,9 @@ struct ModuleConfig {
case proto // = 2
case textmsg // = 3
case nmea // = 4
/// NMEA messages specifically tailored for CalTopo
case caltopo // = 5
case UNRECOGNIZED(Int)
init() {
@ -556,6 +602,7 @@ struct ModuleConfig {
case 2: self = .proto
case 3: self = .textmsg
case 4: self = .nmea
case 5: self = .caltopo
default: self = .UNRECOGNIZED(rawValue)
}
}
@ -567,6 +614,7 @@ struct ModuleConfig {
case .proto: return 2
case .textmsg: return 3
case .nmea: return 4
case .caltopo: return 5
case .UNRECOGNIZED(let i): return i
}
}
@ -933,6 +981,7 @@ extension ModuleConfig.SerialConfig.Serial_Mode: CaseIterable {
.proto,
.textmsg,
.nmea,
.caltopo,
]
}
@ -982,6 +1031,7 @@ extension ModuleConfig: @unchecked Sendable {}
extension ModuleConfig.OneOf_PayloadVariant: @unchecked Sendable {}
extension ModuleConfig.MQTTConfig: @unchecked Sendable {}
extension ModuleConfig.RemoteHardwareConfig: @unchecked Sendable {}
extension ModuleConfig.NeighborInfoConfig: @unchecked Sendable {}
extension ModuleConfig.AudioConfig: @unchecked Sendable {}
extension ModuleConfig.AudioConfig.Audio_Baud: @unchecked Sendable {}
extension ModuleConfig.SerialConfig: @unchecked Sendable {}
@ -1020,6 +1070,7 @@ extension ModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
7: .standard(proto: "canned_message"),
8: .same(proto: "audio"),
9: .standard(proto: "remote_hardware"),
10: .standard(proto: "neighbor_info"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -1145,6 +1196,19 @@ extension ModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
self.payloadVariant = .remoteHardware(v)
}
}()
case 10: try {
var v: ModuleConfig.NeighborInfoConfig?
var hadOneofValue = false
if let current = self.payloadVariant {
hadOneofValue = true
if case .neighborInfo(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
self.payloadVariant = .neighborInfo(v)
}
}()
default: break
}
}
@ -1192,6 +1256,10 @@ extension ModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
guard case .remoteHardware(let v)? = self.payloadVariant else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 9)
}()
case .neighborInfo?: try {
guard case .neighborInfo(let v)? = self.payloadVariant else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 10)
}()
case nil: break
}
try unknownFields.traverse(visitor: &visitor)
@ -1322,6 +1390,44 @@ extension ModuleConfig.RemoteHardwareConfig: SwiftProtobuf.Message, SwiftProtobu
}
}
extension ModuleConfig.NeighborInfoConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = ModuleConfig.protoMessageName + ".NeighborInfoConfig"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "enabled"),
2: .standard(proto: "update_interval"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularBoolField(value: &self.enabled) }()
case 2: try { try decoder.decodeSingularUInt32Field(value: &self.updateInterval) }()
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.enabled != false {
try visitor.visitSingularBoolField(value: self.enabled, fieldNumber: 1)
}
if self.updateInterval != 0 {
try visitor.visitSingularUInt32Field(value: self.updateInterval, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: ModuleConfig.NeighborInfoConfig, rhs: ModuleConfig.NeighborInfoConfig) -> Bool {
if lhs.enabled != rhs.enabled {return false}
if lhs.updateInterval != rhs.updateInterval {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension ModuleConfig.AudioConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = ModuleConfig.protoMessageName + ".AudioConfig"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
@ -1414,6 +1520,7 @@ extension ModuleConfig.SerialConfig: SwiftProtobuf.Message, SwiftProtobuf._Messa
5: .same(proto: "baud"),
6: .same(proto: "timeout"),
7: .same(proto: "mode"),
8: .standard(proto: "override_console_serial_port"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -1429,6 +1536,7 @@ extension ModuleConfig.SerialConfig: SwiftProtobuf.Message, SwiftProtobuf._Messa
case 5: try { try decoder.decodeSingularEnumField(value: &self.baud) }()
case 6: try { try decoder.decodeSingularUInt32Field(value: &self.timeout) }()
case 7: try { try decoder.decodeSingularEnumField(value: &self.mode) }()
case 8: try { try decoder.decodeSingularBoolField(value: &self.overrideConsoleSerialPort) }()
default: break
}
}
@ -1456,6 +1564,9 @@ extension ModuleConfig.SerialConfig: SwiftProtobuf.Message, SwiftProtobuf._Messa
if self.mode != .default {
try visitor.visitSingularEnumField(value: self.mode, fieldNumber: 7)
}
if self.overrideConsoleSerialPort != false {
try visitor.visitSingularBoolField(value: self.overrideConsoleSerialPort, fieldNumber: 8)
}
try unknownFields.traverse(visitor: &visitor)
}
@ -1467,6 +1578,7 @@ extension ModuleConfig.SerialConfig: SwiftProtobuf.Message, SwiftProtobuf._Messa
if lhs.baud != rhs.baud {return false}
if lhs.timeout != rhs.timeout {return false}
if lhs.mode != rhs.mode {return false}
if lhs.overrideConsoleSerialPort != rhs.overrideConsoleSerialPort {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
@ -1500,6 +1612,7 @@ extension ModuleConfig.SerialConfig.Serial_Mode: SwiftProtobuf._ProtoNameProvidi
2: .same(proto: "PROTO"),
3: .same(proto: "TEXTMSG"),
4: .same(proto: "NMEA"),
5: .same(proto: "CALTOPO"),
]
}

View file

@ -60,7 +60,7 @@ struct Connect: View {
Text("ble.name").font(.callout)+Text(": \(bleManager.connectedPeripheral.peripheral.name ?? "unknown".localized)")
.font(.callout).foregroundColor(Color.gray)
if node != nil {
Text("firmware.version").font(.callout)+Text(": \(node?.myInfo?.firmwareVersion ?? "unknown".localized)")
Text("firmware.version").font(.callout)+Text(": \(node?.metadata?.firmwareVersion ?? "unknown".localized)")
.font(.callout).foregroundColor(Color.gray)
}
if bleManager.isSubscribed {
@ -110,8 +110,6 @@ struct Connect: View {
Text("Num: \(String(node!.num))")
Text("Short Name: \(node?.user?.shortName ?? "????")")
Text("Long Name: \(node?.user?.longName ?? "unknown".localized)")
Text("Max Channels: \(String(node?.myInfo?.maxChannels ?? 0))")
Text("Bitrate: \(String(format: "%.2f", node?.myInfo?.bitrate ?? 0.00))")
Text("BLE RSSI: \(bleManager.connectedPeripheral.rssi)")
}
}

View file

@ -35,7 +35,7 @@ struct NodeInfoView: View {
if node.user != nil {
Image(hwModelString)
.resizable()
.aspectRatio(contentMode: .fill)
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
.cornerRadius(5)
@ -104,20 +104,6 @@ struct NodeInfoView: View {
Text(String(node.num)).font(.title).foregroundColor(.gray)
}
Divider()
VStack {
HStack {
Image(systemName: "globe")
.font(.title)
.foregroundColor(.accentColor)
.symbolRenderingMode(.hierarchical)
Text("MAC Address: ").font(.title)
}
Text(String(node.user?.macaddr?.macAddressString ?? "not a valid mac address"))
.font(.title)
.foregroundColor(.gray)
}
Divider()
VStack {
HStack {
Image(systemName: "clock.badge.checkmark.fill")
@ -147,6 +133,7 @@ struct NodeInfoView: View {
VStack {
Image(node.user!.hwModel ?? "unset".localized)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 75, height: 75)
.cornerRadius(5)
Text(String(node.user!.hwModel ?? "unset".localized))
@ -208,16 +195,6 @@ struct NodeInfoView: View {
}
}
Divider()
HStack {
Image(systemName: "globe")
.font(.headline)
.foregroundColor(.accentColor)
.symbolRenderingMode(.hierarchical)
Text("MAC Address: ")
Text(String(node.user?.macaddr?.macAddressString ?? "not a valid mac address")).foregroundColor(.gray)
}
.padding([.bottom], 10)
Divider()
}
VStack {

View file

@ -182,7 +182,7 @@ struct UserMessageList: View {
if message.realACK {
Text("\(ackErrorVal?.display ?? "Empty Ack Error")").font(.caption2).foregroundColor(.gray)
} else {
Text("Implicit ACK from Unknown Node").font(.caption2).foregroundColor(.orange)
Text("Implicit ACK from another node").font(.caption2).foregroundColor(.orange)
}
} else if currentUser && message.ackError == 0 {
// Empty Error

View file

@ -27,7 +27,10 @@ struct EnvironmentMetricsLog: View {
let chartData = environmentMetrics
.filter { $0.time != nil && $0.time! >= oneWeekAgo! }
.sorted { $0.time! < $1.time! }
let locale = NSLocale.current as NSLocale
let localeUnit = locale.object(forKey: NSLocale.Key(rawValue: "kCFLocaleTemperatureUnitKey"))
var format: UnitTemperature = localeUnit as? String ?? "Celsius" == "Fahrenheit" ? .fahrenheit : .celsius
NavigationStack {
if chartData.count > 0 {
@ -69,7 +72,7 @@ struct EnvironmentMetricsLog: View {
.chartXAxis(content: {
AxisMarks(position: .top)
})
.chartYScale(domain: 0...125)
.chartYScale(domain: format == .celsius ? -20...55 : 0...125)
.chartForegroundStyleScale([
"Temperature" : .clear
])

View file

@ -23,6 +23,7 @@ struct AdminMessageList: View {
var body: some View {
let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddjmmssa", options: 0, locale: Locale.current)
let dateFormatString = (localeDateFormat ?? "MM/dd/YY j:mm:ss a")
List {
if user != nil {
@ -47,7 +48,7 @@ struct AdminMessageList: View {
.foregroundColor(am.receivedACK ? .gray : .red)
.font(.caption2)
} else {
Text("Implicit ACK from Unknown Node")
Text("Implicit ACK from another node")
.foregroundColor(.orange)
.font(.caption2)
}

View file

@ -91,7 +91,7 @@ struct Settings: View {
let connectedNode = nodes.first(where: { $0.num == connectedNodeNum })
connectedNodeNum = Int(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral?.num ?? 0 : 0)
if connectedNode != nil && node?.metadata == nil {
if connectedNode != nil && connectedNode?.user != nil && connectedNode?.myInfo != nil && node?.user != nil && node?.metadata == nil {
let adminMessageId = bleManager.requestDeviceMetadata(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode!.myInfo!.adminIndex, context: context)
if adminMessageId > 0 {