mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge pull request #584 from meshtastic/2.3.4_Working_Changes
2.3.4 working changes
This commit is contained in:
commit
f0109c9638
29 changed files with 1412 additions and 460 deletions
|
|
@ -201,6 +201,8 @@
|
|||
DDE5B4042B2279A700FCDD05 /* TraceRouteLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE5B4032B2279A700FCDD05 /* TraceRouteLog.swift */; };
|
||||
DDE5B4062B227E3200FCDD05 /* TraceRouteEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE5B4052B227E3200FCDD05 /* TraceRouteEntityExtension.swift */; };
|
||||
DDE9659C2B1C3B6A00531070 /* RouteRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE9659B2B1C3B6A00531070 /* RouteRecorder.swift */; };
|
||||
DDF45C342BC1A48E005ED5F2 /* MQTTIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF45C332BC1A48E005ED5F2 /* MQTTIcon.swift */; };
|
||||
DDF45C372BC46A5A005ED5F2 /* TimeZone.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF45C362BC46A5A005ED5F2 /* TimeZone.swift */; };
|
||||
DDF6B2482A9AEBF500BA6931 /* StoreForwardConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF6B2472A9AEBF500BA6931 /* StoreForwardConfig.swift */; };
|
||||
DDF924CA26FBB953009FE055 /* ConnectedDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF924C926FBB953009FE055 /* ConnectedDevice.swift */; };
|
||||
DDFEB3BB29900C1200EE7472 /* CurrentConditionsCompact.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDFEB3BA29900C1200EE7472 /* CurrentConditionsCompact.swift */; };
|
||||
|
|
@ -477,6 +479,10 @@
|
|||
DDE5B4052B227E3200FCDD05 /* TraceRouteEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TraceRouteEntityExtension.swift; sourceTree = "<group>"; };
|
||||
DDE9659B2B1C3B6A00531070 /* RouteRecorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouteRecorder.swift; sourceTree = "<group>"; };
|
||||
DDEE03EC29544A1000FCAD57 /* MeshtasticDataModelV4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV4.xcdatamodel; sourceTree = "<group>"; };
|
||||
DDF45C332BC1A48E005ED5F2 /* MQTTIcon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MQTTIcon.swift; sourceTree = "<group>"; };
|
||||
DDF45C352BC465B2005ED5F2 /* se */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = se; path = se.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DDF45C362BC46A5A005ED5F2 /* TimeZone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeZone.swift; sourceTree = "<group>"; };
|
||||
DDF45C382BC46B16005ED5F2 /* MeshtasticDataModelV33.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV33.xcdatamodel; sourceTree = "<group>"; };
|
||||
DDF6B2462A9AEB9E00BA6931 /* MeshtasticDataModelV17.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV17.xcdatamodel; sourceTree = "<group>"; };
|
||||
DDF6B2472A9AEBF500BA6931 /* StoreForwardConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreForwardConfig.swift; sourceTree = "<group>"; };
|
||||
DDF6B24B2A9C2FC800BA6931 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
|
|
@ -907,6 +913,7 @@
|
|||
DDC2E18D26CE25CB0042C5E4 /* Helpers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DDF45C332BC1A48E005ED5F2 /* MQTTIcon.swift */,
|
||||
DD5E523D298F5A7D00D21B61 /* Weather */,
|
||||
DD47E3D526F17ED900029299 /* CircleText.swift */,
|
||||
DDF924C926FBB953009FE055 /* ConnectedDevice.swift */,
|
||||
|
|
@ -990,6 +997,7 @@
|
|||
DDB75A102A059258006ED576 /* Url.swift */,
|
||||
DD1933772B084F4200771CD5 /* Measurement.swift */,
|
||||
DDFFA7462B3A7F3C004730DB /* Bundle.swift */,
|
||||
DDF45C362BC46A5A005ED5F2 /* TimeZone.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1138,6 +1146,7 @@
|
|||
he,
|
||||
fr,
|
||||
"zh-Hant-TW",
|
||||
se,
|
||||
);
|
||||
mainGroup = DDC2E14B26CE248E0042C5E4;
|
||||
packageReferences = (
|
||||
|
|
@ -1283,6 +1292,7 @@
|
|||
6DEDA55C2A9592F900321D2E /* MessageEntityExtension.swift in Sources */,
|
||||
DDDB444229F8A88700EE2349 /* Double.swift in Sources */,
|
||||
DD5E520F298EE33B00D21B61 /* cannedmessages.pb.swift in Sources */,
|
||||
DDF45C342BC1A48E005ED5F2 /* MQTTIcon.swift in Sources */,
|
||||
DDB75A232A13CDA9006ED576 /* BatteryLevelCompact.swift in Sources */,
|
||||
DDB75A162A0594AD006ED576 /* TileOverlay.swift in Sources */,
|
||||
DDF924CA26FBB953009FE055 /* ConnectedDevice.swift in Sources */,
|
||||
|
|
@ -1291,6 +1301,7 @@
|
|||
DD3619152B1EF9F900C41C8C /* LocationsHandler.swift in Sources */,
|
||||
DDDB444A29F8AA3A00EE2349 /* CLLocationCoordinate2D.swift in Sources */,
|
||||
DD41582628582E9B009B0E59 /* DeviceConfig.swift in Sources */,
|
||||
DDF45C372BC46A5A005ED5F2 /* TimeZone.swift in Sources */,
|
||||
DD007BAE2AA4E91200F5FA12 /* MyInfoEntityExtension.swift in Sources */,
|
||||
DD33DB622B3D27C7003E1EA0 /* FirmwareApi.swift in Sources */,
|
||||
DD3CC6B528E33FD100FA9159 /* ShareChannels.swift in Sources */,
|
||||
|
|
@ -1461,6 +1472,7 @@
|
|||
DD31EC492B7F18B7006A3995 /* he */,
|
||||
DDDC22312BA76701002C44F1 /* fr */,
|
||||
DDDC22322BA76961002C44F1 /* zh-Hant-TW */,
|
||||
DDF45C352BC465B2005ED5F2 /* se */,
|
||||
);
|
||||
name = Localizable.strings;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1611,7 +1623,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.3.3;
|
||||
MARKETING_VERSION = 2.3.4;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -1645,7 +1657,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.3.3;
|
||||
MARKETING_VERSION = 2.3.4;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -1767,7 +1779,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.3.3;
|
||||
MARKETING_VERSION = 2.3.4;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
@ -1800,7 +1812,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.3.3;
|
||||
MARKETING_VERSION = 2.3.4;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
@ -1911,6 +1923,7 @@
|
|||
DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
DDF45C382BC46B16005ED5F2 /* MeshtasticDataModelV33.xcdatamodel */,
|
||||
DD9681A22BBB22BE00FD2C47 /* MeshtasticDataModelV32.xcdatamodel */,
|
||||
DDDCD5712BB3246500BE6B60 /* MeshtasticDataModelV 31.xcdatamodel */,
|
||||
DD9A1A912BA2D2D3001E602E /* MeshtasticDataModelV 30.xcdatamodel */,
|
||||
|
|
@ -1944,7 +1957,7 @@
|
|||
DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */,
|
||||
DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */,
|
||||
);
|
||||
currentVersion = DD9681A22BBB22BE00FD2C47 /* MeshtasticDataModelV32.xcdatamodel */;
|
||||
currentVersion = DDF45C382BC46B16005ED5F2 /* MeshtasticDataModelV33.xcdatamodel */;
|
||||
name = Meshtastic.xcdatamodeld;
|
||||
path = Meshtastic/Meshtastic.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
|
|
|
|||
72
Meshtastic/Extensions/TimeZone.swift
Normal file
72
Meshtastic/Extensions/TimeZone.swift
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
//
|
||||
// TimeZone.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright(C) Garth Vander Houwen 4/8/24.
|
||||
//
|
||||
import Foundation
|
||||
|
||||
extension TimeZone {
|
||||
var posixDescription: String {
|
||||
if let nextDate = nextDaylightSavingTimeTransition, let afterDate = nextDaylightSavingTimeTransition(after: nextDate) {
|
||||
// This timezone observes DST
|
||||
|
||||
// Get the transition dates to/from standard/DST
|
||||
let stdDate: Date
|
||||
let dstDate: Date
|
||||
if isDaylightSavingTime(for: nextDate) {
|
||||
stdDate = afterDate
|
||||
dstDate = nextDate
|
||||
} else {
|
||||
stdDate = nextDate
|
||||
dstDate = afterDate
|
||||
}
|
||||
|
||||
// Append the standard abbreviation
|
||||
var res = posixAbbreviation(for: stdDate)
|
||||
// Append the standard offset
|
||||
res += posixOffset(for: stdDate)
|
||||
// Append the DST abbreviation
|
||||
res += posixAbbreviation(for: dstDate)
|
||||
|
||||
// Append the DST offset if it's not 1 hour different
|
||||
let diff = secondsFromGMT(for: stdDate) - secondsFromGMT(for: dstDate)
|
||||
if abs(diff) != 3600 {
|
||||
res += posixOffset(for: dstDate)
|
||||
}
|
||||
|
||||
// Get month, weekday ordinal, weekday, hour, minutes, and second
|
||||
// weekday gets returned as 1-based but we need 0-based
|
||||
// The hour is based on the post-transition time but we need the pre-transition time
|
||||
var cal = Calendar(identifier: .gregorian)
|
||||
cal.timeZone = self
|
||||
let stdcomps = cal.dateComponents([.month, .weekdayOrdinal, .weekday, .hour, .minute, .second], from: stdDate)
|
||||
let dstcomps = cal.dateComponents([.month, .weekdayOrdinal, .weekday, .hour, .minute, .second], from: dstDate)
|
||||
|
||||
res += String(format: ",M%d.%d.%d/%d:%02d:%02d", dstcomps.month!, dstcomps.weekdayOrdinal!, dstcomps.weekday! - 1, dstcomps.hour! - 1, dstcomps.minute!, dstcomps.second!)
|
||||
res += String(format: ",M%d.%d.%d/%d:%02d:%02d", stdcomps.month!, stdcomps.weekdayOrdinal!, stdcomps.weekday! - 1, stdcomps.hour! + 1, stdcomps.minute!, stdcomps.second!)
|
||||
|
||||
return res
|
||||
} else {
|
||||
// This timezone does not observe DST
|
||||
return "\(posixAbbreviation())\(posixOffset())"
|
||||
}
|
||||
}
|
||||
|
||||
private func posixAbbreviation(for date: Date = Date()) -> String {
|
||||
let abrev = abbreviation(for: date) ?? "<UNK>" // We never actually get "<UNK>" for any TimeZone identifier
|
||||
// Many abbreviations come in the form "GMT+X" or "GMT-X"
|
||||
return abrev.hasPrefix("GMT") ? "GMT" : abrev
|
||||
}
|
||||
|
||||
private func posixOffset(for date: Date = Date()) -> String {
|
||||
// The POSIX offset is the opposite of the GMT offset
|
||||
let secs = 0 - secondsFromGMT(for: date)
|
||||
let h = secs / 3600
|
||||
let m = abs(secs) % 3600 / 60
|
||||
let s = abs(secs) % 60
|
||||
|
||||
// Show the hour, only show the minutes and seconds if non-zero
|
||||
return "\(h)\(m == 0 && s == 0 ? "" : ":\(String(format: "%02d", m))")\(s == 0 ? "" : ":\(String(format: "%02d", s))")"
|
||||
}
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
@Published var isSwitchedOn: Bool = false
|
||||
@Published var automaticallyReconnect: Bool = true
|
||||
@Published var mqttProxyConnected: Bool = false
|
||||
@Published var mqttError: String = ""
|
||||
@StateObject var appState = AppState.shared
|
||||
public var minimumVersion = "2.0.0"
|
||||
public var connectedVersion: String
|
||||
|
|
@ -312,6 +313,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
// MARK: MqttClientProxyManagerDelegate Methods
|
||||
func onMqttConnected() {
|
||||
mqttProxyConnected = true
|
||||
mqttError = ""
|
||||
print("📲 Mqtt Client Proxy onMqttConnected now subscribing to \(mqttManager.topic).")
|
||||
mqttManager.mqttClientProxy?.subscribe(mqttManager.topic)
|
||||
}
|
||||
|
|
@ -344,6 +346,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
|
||||
func onMqttError(message: String) {
|
||||
mqttProxyConnected = false
|
||||
mqttError = message
|
||||
print("📲 Mqtt Client Proxy onMqttError: \(message)")
|
||||
}
|
||||
|
||||
|
|
@ -415,7 +418,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
// Grab the most recent postion, within the last hour
|
||||
if connectedNode?.positions?.count ?? 0 > 0 {
|
||||
let mostRecent = connectedNode?.positions?.lastObject as! PositionEntity
|
||||
if mostRecent.time! >= Calendar.current.date(byAdding: .minute, value: -60, to: Date())! {
|
||||
if mostRecent.time! >= Calendar.current.date(byAdding: .hour, value: -24, to: Date())! {
|
||||
traceRoute.altitude = mostRecent.altitude
|
||||
traceRoute.latitudeI = mostRecent.latitudeI
|
||||
traceRoute.longitudeI = mostRecent.longitudeI
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>MeshtasticDataModelV32.xcdatamodel</string>
|
||||
<string>MeshtasticDataModelV33.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,462 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22757" systemVersion="23E224" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="AmbientLightingConfigEntity" representedClassName="AmbientLightingConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="blue" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="current" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="green" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ledState" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="red" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="ambientLightingConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="ambientLightingConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<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" attributeType="Boolean" defaultValueString="NO" 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="positionPrecision" optional="YES" attributeType="Integer 32" defaultValueString="32" usesScalarValueType="YES"/>
|
||||
<attribute name="psk" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="role" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="uplinkEnabled" attributeType="Boolean" defaultValueString="NO" 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 && toUser == nil AND isEmoji == false"/>
|
||||
</fetchedProperty>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="index"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="DetectionSensorConfigEntity" representedClassName="DetectionSensorConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="detectionTriggeredHigh" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="minimumBroadcastSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="monitorPin" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String"/>
|
||||
<attribute name="sendBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="stateBroadcastSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="usePullup" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<relationship name="detectionSensorConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="detectionSensorConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</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="disableTripleClick" 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"/>
|
||||
<attribute name="tzdef" optional="YES" attributeType="String"/>
|
||||
<relationship name="deviceConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="deviceConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="DeviceHardwareEntity" representedClassName="DeviceHardwareEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="activelySupported" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="architecture" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="displayName" optional="YES" attributeType="String"/>
|
||||
<attribute name="hwModel" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="hwModelSlug" optional="YES" attributeType="String"/>
|
||||
<attribute name="platformioTarget" optional="YES" attributeType="String"/>
|
||||
</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="units" 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="useI2SAsBuzzer" optional="YES" attributeType="Boolean" 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"/>
|
||||
<fetchedProperty name="fetchedProperty" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="ExternalNotificationConfigEntity"/>
|
||||
</fetchedProperty>
|
||||
</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" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<attribute name="latitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="longitudeI" 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="ignoreMqtt" optional="YES" attributeType="Boolean" defaultValueString="NO" 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="portNum" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="read" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="realACK" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="receivedACK" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="receivedTimestamp" optional="YES" attributeType="Integer 32" defaultValueString="0" 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="mapPositionPrecision" optional="YES" attributeType="Integer 32" defaultValueString="13" usesScalarValueType="YES"/>
|
||||
<attribute name="mapPublishIntervalSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="mapReportingEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="password" optional="YES" attributeType="String" maxValueString="30"/>
|
||||
<attribute name="proxyToClientEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<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"/>
|
||||
<fetchedProperty name="allMessages" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="toUser == nil"/>
|
||||
</fetchedProperty>
|
||||
<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="favorite" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="hopsAway" 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"/>
|
||||
<attribute name="viaMqtt" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<relationship name="ambientLightingConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="AmbientLightingConfigEntity" inverseName="ambientLightingConfigNode" inverseEntity="AmbientLightingConfigEntity"/>
|
||||
<relationship name="bluetoothConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="BluetoothConfigEntity" inverseName="bluetoothConfigNode" inverseEntity="BluetoothConfigEntity"/>
|
||||
<relationship name="cannedMessageConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="CannedMessageConfigEntity" inverseName="cannedMessagesConfigNode" inverseEntity="CannedMessageConfigEntity"/>
|
||||
<relationship name="detectionSensorConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="DetectionSensorConfigEntity" inverseName="detectionSensorConfigNode" inverseEntity="DetectionSensorConfigEntity"/>
|
||||
<relationship name="deviceConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="DeviceConfigEntity" inverseName="deviceConfigNode" inverseEntity="DeviceConfigEntity"/>
|
||||
<relationship name="displayConfig" optional="YES" maxCount="1" deletionRule="Cascade" 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="pax" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PaxCounterEntity" inverseName="paxNode" inverseEntity="PaxCounterEntity"/>
|
||||
<relationship name="paxCounterConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PaxCounterConfigEntity" inverseName="paxCounterConfigNode" inverseEntity="PaxCounterConfigEntity"/>
|
||||
<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="powerConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PowerConfigEntity" inverseName="powerConfigNode" inverseEntity="PowerConfigEntity"/>
|
||||
<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="storeForwardConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="StoreForwardConfigEntity" inverseName="storeForwardConfigNode" inverseEntity="StoreForwardConfigEntity"/>
|
||||
<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="traceRoutes" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TraceRouteEntity" inverseName="node" inverseEntity="TraceRouteEntity"/>
|
||||
<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="PaxCounterConfigEntity" representedClassName="PaxCounterConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="paxcounterUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="paxCounterConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="paxCounterConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="PaxCounterEntity" representedClassName="PaxCounterEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="ble" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="uptime" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="wifi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="paxNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="pax" inverseEntity="NodeInfoEntity"/>
|
||||
</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="gpsEnGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsMode" 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="precisionBits" optional="YES" attributeType="Integer 32" defaultValueString="32" 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="PowerConfigEntity" representedClassName="PowerConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="adcMultiplierOverride" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
|
||||
<attribute name="deviceBatteryInaAddress" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="isPowerSaving" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="lsSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="minWakeSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="onBatteryShutdownAfterSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="waitBluetoothSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="powerConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="powerConfig" 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 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<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" toMany="YES" deletionRule="Cascade" ordered="YES" 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="overrideConsoleSerialPort" optional="YES" attributeType="Boolean" defaultValueString="NO" 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="StoreForwardConfigEntity" representedClassName="StoreForwardConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="heartbeat" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="historyReturnMax" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="historyReturnWindow" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="isRouter" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="lastHeartbeat" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="lastRequest" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="records" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="storeForwardConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="storeForwardConfig" 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"/>
|
||||
<attribute name="powerMeasurementEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="powerScreenEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="powerUpdateInterval" 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="TraceRouteEntity" representedClassName="TraceRouteEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="hasPositions" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="id" optional="YES" attributeType="Integer 64" defaultValueString="0" 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="num" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="response" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="route" optional="YES" attributeType="Transformable" customClassName="[UInt32]"/>
|
||||
<attribute name="routeText" optional="YES" attributeType="String"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="hops" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TraceRouteHopEntity" inverseName="traceRoute" inverseEntity="TraceRouteHopEntity"/>
|
||||
<relationship name="node" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="traceRoutes" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="TraceRouteHopEntity" representedClassName="TraceRouteHopEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" 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="name" optional="YES" attributeType="String"/>
|
||||
<attribute name="num" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="traceRoute" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TraceRouteEntity" inverseName="hops" inverseEntity="TraceRouteEntity"/>
|
||||
</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="lastMessage" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<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="numString" optional="YES" attributeType="String"/>
|
||||
<attribute name="role" attributeType="Integer 32" 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 AND portNum != 10 "/>
|
||||
</fetchedProperty>
|
||||
<fetchedProperty name="detectionSensorMessages" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="(fromUser.num == $FETCH_SOURCE.num) AND portNum = 10"/>
|
||||
</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"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="id"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
</model>
|
||||
|
|
@ -437,6 +437,7 @@ func upsertDeviceConfigPacket(config: Meshtastic.Config.DeviceConfig, nodeNum: I
|
|||
newDeviceConfig.nodeInfoBroadcastSecs = Int32(truncating: config.nodeInfoBroadcastSecs as NSNumber)
|
||||
newDeviceConfig.doubleTapAsButtonPress = config.doubleTapAsButtonPress
|
||||
newDeviceConfig.isManaged = config.isManaged
|
||||
newDeviceConfig.tzdef = config.tzdef
|
||||
fetchedNode[0].deviceConfig = newDeviceConfig
|
||||
} else {
|
||||
fetchedNode[0].deviceConfig?.role = Int32(config.role.rawValue)
|
||||
|
|
@ -448,6 +449,7 @@ func upsertDeviceConfigPacket(config: Meshtastic.Config.DeviceConfig, nodeNum: I
|
|||
fetchedNode[0].deviceConfig?.nodeInfoBroadcastSecs = Int32(truncating: config.nodeInfoBroadcastSecs as NSNumber)
|
||||
fetchedNode[0].deviceConfig?.doubleTapAsButtonPress = config.doubleTapAsButtonPress
|
||||
fetchedNode[0].deviceConfig?.isManaged = config.isManaged
|
||||
fetchedNode[0].deviceConfig?.tzdef = config.tzdef
|
||||
}
|
||||
do {
|
||||
try context.save()
|
||||
|
|
|
|||
|
|
@ -190,6 +190,10 @@ struct Config {
|
|||
/// Disables the triple-press of user button to enable or disable GPS
|
||||
var disableTripleClick: Bool = false
|
||||
|
||||
///
|
||||
/// POSIX Timezone definition string from https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv.
|
||||
var tzdef: String = String()
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
///
|
||||
|
|
@ -1734,6 +1738,7 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl
|
|||
8: .standard(proto: "double_tap_as_button_press"),
|
||||
9: .standard(proto: "is_managed"),
|
||||
10: .standard(proto: "disable_triple_click"),
|
||||
11: .same(proto: "tzdef"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -1752,6 +1757,7 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl
|
|||
case 8: try { try decoder.decodeSingularBoolField(value: &self.doubleTapAsButtonPress) }()
|
||||
case 9: try { try decoder.decodeSingularBoolField(value: &self.isManaged) }()
|
||||
case 10: try { try decoder.decodeSingularBoolField(value: &self.disableTripleClick) }()
|
||||
case 11: try { try decoder.decodeSingularStringField(value: &self.tzdef) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -1788,6 +1794,9 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl
|
|||
if self.disableTripleClick != false {
|
||||
try visitor.visitSingularBoolField(value: self.disableTripleClick, fieldNumber: 10)
|
||||
}
|
||||
if !self.tzdef.isEmpty {
|
||||
try visitor.visitSingularStringField(value: self.tzdef, fieldNumber: 11)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
|
|
@ -1802,6 +1811,7 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl
|
|||
if lhs.doubleTapAsButtonPress != rhs.doubleTapAsButtonPress {return false}
|
||||
if lhs.isManaged != rhs.isManaged {return false}
|
||||
if lhs.disableTripleClick != rhs.disableTripleClick {return false}
|
||||
if lhs.tzdef != rhs.tzdef {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
}
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// Font sizes for the device screen
|
||||
enum ScreenFonts: SwiftProtobuf.Enum {
|
||||
typealias RawValue = Int
|
||||
|
||||
|
|
@ -75,6 +75,140 @@ extension ScreenFonts: CaseIterable {
|
|||
|
||||
#endif // swift(>=4.2)
|
||||
|
||||
///
|
||||
/// 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() {}
|
||||
}
|
||||
|
||||
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}
|
||||
}
|
||||
|
||||
///
|
||||
/// True if we witnessed the node over MQTT instead of LoRA transport
|
||||
var viaMqtt: Bool {
|
||||
get {return _storage._viaMqtt}
|
||||
set {_uniqueStorage()._viaMqtt = newValue}
|
||||
}
|
||||
|
||||
///
|
||||
/// Number of hops away from us this node is (0 if adjacent)
|
||||
var hopsAway: UInt32 {
|
||||
get {return _storage._hopsAway}
|
||||
set {_uniqueStorage()._hopsAway = newValue}
|
||||
}
|
||||
|
||||
///
|
||||
/// True if node is in our favorites list
|
||||
/// Persists between NodeDB internal clean ups
|
||||
var isFavorite: Bool {
|
||||
get {return _storage._isFavorite}
|
||||
set {_uniqueStorage()._isFavorite = newValue}
|
||||
}
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
|
||||
fileprivate var _storage = _StorageClass.defaultInstance
|
||||
}
|
||||
|
||||
///
|
||||
/// This message is never sent over the wire, but it is used for serializing DB
|
||||
/// state to flash in the device code
|
||||
|
|
@ -187,140 +321,6 @@ 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}
|
||||
}
|
||||
|
||||
///
|
||||
/// True if we witnessed the node over MQTT instead of LoRA transport
|
||||
var viaMqtt: Bool {
|
||||
get {return _storage._viaMqtt}
|
||||
set {_uniqueStorage()._viaMqtt = newValue}
|
||||
}
|
||||
|
||||
///
|
||||
/// Number of hops away from us this node is (0 if adjacent)
|
||||
var hopsAway: UInt32 {
|
||||
get {return _storage._hopsAway}
|
||||
set {_uniqueStorage()._hopsAway = newValue}
|
||||
}
|
||||
|
||||
///
|
||||
/// True if node is in our favorites list
|
||||
/// Persists between NodeDB internal clean ups
|
||||
var isFavorite: Bool {
|
||||
get {return _storage._isFavorite}
|
||||
set {_uniqueStorage()._isFavorite = 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 {
|
||||
|
|
@ -407,9 +407,9 @@ struct OEMStore {
|
|||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension ScreenFonts: @unchecked Sendable {}
|
||||
extension DeviceState: @unchecked Sendable {}
|
||||
extension NodeInfoLite: @unchecked Sendable {}
|
||||
extension PositionLite: @unchecked Sendable {}
|
||||
extension NodeInfoLite: @unchecked Sendable {}
|
||||
extension DeviceState: @unchecked Sendable {}
|
||||
extension ChannelFile: @unchecked Sendable {}
|
||||
extension OEMStore: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
|
@ -426,141 +426,57 @@ extension ScreenFonts: SwiftProtobuf._ProtoNameProviding {
|
|||
]
|
||||
}
|
||||
|
||||
extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".DeviceState"
|
||||
extension PositionLite: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".PositionLite"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
2: .standard(proto: "my_node"),
|
||||
3: .same(proto: "owner"),
|
||||
5: .standard(proto: "receive_queue"),
|
||||
8: .same(proto: "version"),
|
||||
7: .standard(proto: "rx_text_message"),
|
||||
9: .standard(proto: "no_save"),
|
||||
11: .standard(proto: "did_gps_reset"),
|
||||
12: .standard(proto: "rx_waypoint"),
|
||||
13: .standard(proto: "node_remote_hardware_pins"),
|
||||
14: .standard(proto: "node_db_lite"),
|
||||
1: .standard(proto: "latitude_i"),
|
||||
2: .standard(proto: "longitude_i"),
|
||||
3: .same(proto: "altitude"),
|
||||
4: .same(proto: "time"),
|
||||
5: .standard(proto: "location_source"),
|
||||
]
|
||||
|
||||
fileprivate class _StorageClass {
|
||||
var _myNode: MyNodeInfo? = nil
|
||||
var _owner: User? = nil
|
||||
var _receiveQueue: [MeshPacket] = []
|
||||
var _version: UInt32 = 0
|
||||
var _rxTextMessage: MeshPacket? = nil
|
||||
var _noSave: Bool = false
|
||||
var _didGpsReset: Bool = false
|
||||
var _rxWaypoint: MeshPacket? = nil
|
||||
var _nodeRemoteHardwarePins: [NodeRemoteHardwarePin] = []
|
||||
var _nodeDbLite: [NodeInfoLite] = []
|
||||
|
||||
static let defaultInstance = _StorageClass()
|
||||
|
||||
private init() {}
|
||||
|
||||
init(copying source: _StorageClass) {
|
||||
_myNode = source._myNode
|
||||
_owner = source._owner
|
||||
_receiveQueue = source._receiveQueue
|
||||
_version = source._version
|
||||
_rxTextMessage = source._rxTextMessage
|
||||
_noSave = source._noSave
|
||||
_didGpsReset = source._didGpsReset
|
||||
_rxWaypoint = source._rxWaypoint
|
||||
_nodeRemoteHardwarePins = source._nodeRemoteHardwarePins
|
||||
_nodeDbLite = source._nodeDbLite
|
||||
}
|
||||
}
|
||||
|
||||
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 2: try { try decoder.decodeSingularMessageField(value: &_storage._myNode) }()
|
||||
case 3: try { try decoder.decodeSingularMessageField(value: &_storage._owner) }()
|
||||
case 5: try { try decoder.decodeRepeatedMessageField(value: &_storage._receiveQueue) }()
|
||||
case 7: try { try decoder.decodeSingularMessageField(value: &_storage._rxTextMessage) }()
|
||||
case 8: try { try decoder.decodeSingularUInt32Field(value: &_storage._version) }()
|
||||
case 9: try { try decoder.decodeSingularBoolField(value: &_storage._noSave) }()
|
||||
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
|
||||
}
|
||||
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 {
|
||||
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
|
||||
try { if let v = _storage._myNode {
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
|
||||
} }()
|
||||
try { if let v = _storage._owner {
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 3)
|
||||
} }()
|
||||
if !_storage._receiveQueue.isEmpty {
|
||||
try visitor.visitRepeatedMessageField(value: _storage._receiveQueue, fieldNumber: 5)
|
||||
}
|
||||
try { if let v = _storage._rxTextMessage {
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 7)
|
||||
} }()
|
||||
if _storage._version != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: _storage._version, fieldNumber: 8)
|
||||
}
|
||||
if _storage._noSave != false {
|
||||
try visitor.visitSingularBoolField(value: _storage._noSave, fieldNumber: 9)
|
||||
}
|
||||
if _storage._didGpsReset != false {
|
||||
try visitor.visitSingularBoolField(value: _storage._didGpsReset, fieldNumber: 11)
|
||||
}
|
||||
try { if let v = _storage._rxWaypoint {
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 12)
|
||||
} }()
|
||||
if !_storage._nodeRemoteHardwarePins.isEmpty {
|
||||
try visitor.visitRepeatedMessageField(value: _storage._nodeRemoteHardwarePins, fieldNumber: 13)
|
||||
}
|
||||
if !_storage._nodeDbLite.isEmpty {
|
||||
try visitor.visitRepeatedMessageField(value: _storage._nodeDbLite, fieldNumber: 14)
|
||||
}
|
||||
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: DeviceState, rhs: DeviceState) -> 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._myNode != rhs_storage._myNode {return false}
|
||||
if _storage._owner != rhs_storage._owner {return false}
|
||||
if _storage._receiveQueue != rhs_storage._receiveQueue {return false}
|
||||
if _storage._version != rhs_storage._version {return false}
|
||||
if _storage._rxTextMessage != rhs_storage._rxTextMessage {return false}
|
||||
if _storage._noSave != rhs_storage._noSave {return false}
|
||||
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}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
|
@ -706,57 +622,141 @@ extension NodeInfoLite: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
|
|||
}
|
||||
}
|
||||
|
||||
extension PositionLite: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".PositionLite"
|
||||
extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".DeviceState"
|
||||
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"),
|
||||
2: .standard(proto: "my_node"),
|
||||
3: .same(proto: "owner"),
|
||||
5: .standard(proto: "receive_queue"),
|
||||
8: .same(proto: "version"),
|
||||
7: .standard(proto: "rx_text_message"),
|
||||
9: .standard(proto: "no_save"),
|
||||
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 {
|
||||
var _myNode: MyNodeInfo? = nil
|
||||
var _owner: User? = nil
|
||||
var _receiveQueue: [MeshPacket] = []
|
||||
var _version: UInt32 = 0
|
||||
var _rxTextMessage: MeshPacket? = nil
|
||||
var _noSave: Bool = false
|
||||
var _didGpsReset: Bool = false
|
||||
var _rxWaypoint: MeshPacket? = nil
|
||||
var _nodeRemoteHardwarePins: [NodeRemoteHardwarePin] = []
|
||||
var _nodeDbLite: [NodeInfoLite] = []
|
||||
|
||||
static let defaultInstance = _StorageClass()
|
||||
|
||||
private init() {}
|
||||
|
||||
init(copying source: _StorageClass) {
|
||||
_myNode = source._myNode
|
||||
_owner = source._owner
|
||||
_receiveQueue = source._receiveQueue
|
||||
_version = source._version
|
||||
_rxTextMessage = source._rxTextMessage
|
||||
_noSave = source._noSave
|
||||
_didGpsReset = source._didGpsReset
|
||||
_rxWaypoint = source._rxWaypoint
|
||||
_nodeRemoteHardwarePins = source._nodeRemoteHardwarePins
|
||||
_nodeDbLite = source._nodeDbLite
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate mutating func _uniqueStorage() -> _StorageClass {
|
||||
if !isKnownUniquelyReferenced(&_storage) {
|
||||
_storage = _StorageClass(copying: _storage)
|
||||
}
|
||||
return _storage
|
||||
}
|
||||
|
||||
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
|
||||
_ = _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 2: try { try decoder.decodeSingularMessageField(value: &_storage._myNode) }()
|
||||
case 3: try { try decoder.decodeSingularMessageField(value: &_storage._owner) }()
|
||||
case 5: try { try decoder.decodeRepeatedMessageField(value: &_storage._receiveQueue) }()
|
||||
case 7: try { try decoder.decodeSingularMessageField(value: &_storage._rxTextMessage) }()
|
||||
case 8: try { try decoder.decodeSingularUInt32Field(value: &_storage._version) }()
|
||||
case 9: try { try decoder.decodeSingularBoolField(value: &_storage._noSave) }()
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 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
|
||||
try { if let v = _storage._myNode {
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
|
||||
} }()
|
||||
try { if let v = _storage._owner {
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 3)
|
||||
} }()
|
||||
if !_storage._receiveQueue.isEmpty {
|
||||
try visitor.visitRepeatedMessageField(value: _storage._receiveQueue, fieldNumber: 5)
|
||||
}
|
||||
try { if let v = _storage._rxTextMessage {
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 7)
|
||||
} }()
|
||||
if _storage._version != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: _storage._version, fieldNumber: 8)
|
||||
}
|
||||
if _storage._noSave != false {
|
||||
try visitor.visitSingularBoolField(value: _storage._noSave, fieldNumber: 9)
|
||||
}
|
||||
if _storage._didGpsReset != false {
|
||||
try visitor.visitSingularBoolField(value: _storage._didGpsReset, fieldNumber: 11)
|
||||
}
|
||||
try { if let v = _storage._rxWaypoint {
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 12)
|
||||
} }()
|
||||
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)
|
||||
}
|
||||
|
||||
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}
|
||||
static func ==(lhs: DeviceState, rhs: DeviceState) -> 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._myNode != rhs_storage._myNode {return false}
|
||||
if _storage._owner != rhs_storage._owner {return false}
|
||||
if _storage._receiveQueue != rhs_storage._receiveQueue {return false}
|
||||
if _storage._version != rhs_storage._version {return false}
|
||||
if _storage._rxTextMessage != rhs_storage._rxTextMessage {return false}
|
||||
if _storage._noSave != rhs_storage._noSave {return false}
|
||||
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}
|
||||
}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -256,6 +256,15 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
/// Older "V1.0" Variant
|
||||
case heltecWirelessTrackerV10 // = 58
|
||||
|
||||
///
|
||||
/// unPhone with ESP32-S3, TFT touchscreen, LSM6DS3TR-C accelerometer and gyroscope
|
||||
case unphone // = 59
|
||||
|
||||
///
|
||||
/// Teledatics TD-LORAC NRF52840 based M.2 LoRA module
|
||||
/// Compatible with the TD-WRLS development board
|
||||
case tdLorac // = 60
|
||||
|
||||
///
|
||||
/// ------------------------------------------------------------------------------------------------------------------------------------------
|
||||
/// 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.
|
||||
|
|
@ -323,6 +332,8 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
case 56: self = .chatter2
|
||||
case 57: self = .heltecWirelessPaperV10
|
||||
case 58: self = .heltecWirelessTrackerV10
|
||||
case 59: self = .unphone
|
||||
case 60: self = .tdLorac
|
||||
case 255: self = .privateHw
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
}
|
||||
|
|
@ -384,6 +395,8 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
case .chatter2: return 56
|
||||
case .heltecWirelessPaperV10: return 57
|
||||
case .heltecWirelessTrackerV10: return 58
|
||||
case .unphone: return 59
|
||||
case .tdLorac: return 60
|
||||
case .privateHw: return 255
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
}
|
||||
|
|
@ -450,6 +463,8 @@ extension HardwareModel: CaseIterable {
|
|||
.chatter2,
|
||||
.heltecWirelessPaperV10,
|
||||
.heltecWirelessTrackerV10,
|
||||
.unphone,
|
||||
.tdLorac,
|
||||
.privateHw,
|
||||
]
|
||||
}
|
||||
|
|
@ -2745,6 +2760,8 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding {
|
|||
56: .same(proto: "CHATTER_2"),
|
||||
57: .same(proto: "HELTEC_WIRELESS_PAPER_V1_0"),
|
||||
58: .same(proto: "HELTEC_WIRELESS_TRACKER_V1_0"),
|
||||
59: .same(proto: "UNPHONE"),
|
||||
60: .same(proto: "TD_LORAC"),
|
||||
255: .same(proto: "PRIVATE_HW"),
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ struct Connect: View {
|
|||
}.padding([.bottom, .top])
|
||||
}
|
||||
}
|
||||
.confirmationDialog("Connecting to a new radio will clear all local app data on the phone and will reset all app specific settings.", isPresented: $presentingSwitchPreferredPeripheral, titleVisibility: .visible) {
|
||||
.confirmationDialog("Connecting to a new radio will clear all local app data on the phone.", isPresented: $presentingSwitchPreferredPeripheral, titleVisibility: .visible) {
|
||||
|
||||
Button("Connect to new radio?", role: .destructive) {
|
||||
UserDefaults.preferredPeripheralId = selectedPeripherialId
|
||||
|
|
@ -218,7 +218,6 @@ struct Connect: View {
|
|||
bleManager.disconnectPeripheral()
|
||||
}
|
||||
clearCoreDataDatabase(context: context)
|
||||
UserDefaults.standard.reset()
|
||||
|
||||
let radio = bleManager.peripherals.first(where: { $0.peripheral.identifier.uuidString == selectedPeripherialId })
|
||||
if radio != nil {
|
||||
|
|
@ -260,7 +259,7 @@ struct Connect: View {
|
|||
.navigationTitle("bluetooth")
|
||||
.navigationBarItems(leading: MeshtasticLogo(), trailing:
|
||||
ZStack {
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?", mqttProxyConnected: bleManager.mqttProxyConnected)
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?", mqttProxyConnected: bleManager.mqttProxyConnected, mqttTopic: bleManager.mqttManager.topic)
|
||||
})
|
||||
}
|
||||
.sheet(isPresented: $invalidFirmwareVersion, onDismiss: didDismissSheet) {
|
||||
|
|
|
|||
|
|
@ -5,56 +5,58 @@ A view draws the indicator used in the upper right corner for views using BLE
|
|||
|
||||
import SwiftUI
|
||||
|
||||
|
||||
struct ConnectedDevice: View {
|
||||
var bluetoothOn: Bool
|
||||
var deviceConnected: Bool
|
||||
var name: String
|
||||
var mqttProxyEnabled: Bool = false
|
||||
var mqttProxyConnected: Bool = false
|
||||
var phoneOnly: Bool = false
|
||||
|
||||
var mqttProxyConnected: Bool = false
|
||||
var mqttUplinkEnabled: Bool = false
|
||||
var mqttDownlinkEnabled: Bool = false
|
||||
var mqttTopic: String = ""
|
||||
var phoneOnly: Bool = false
|
||||
|
||||
var body: some View {
|
||||
|
||||
HStack {
|
||||
|
||||
if (phoneOnly && UIDevice.current.userInterfaceIdiom == .phone) || !phoneOnly {
|
||||
if bluetoothOn {
|
||||
if deviceConnected && (mqttProxyEnabled || mqttProxyConnected) {
|
||||
if (mqttProxyConnected || mqttProxyEnabled) {
|
||||
Image(systemName: "iphone.gen3.radiowaves.left.and.right.circle.fill")
|
||||
.imageScale(.large)
|
||||
.foregroundColor(mqttProxyConnected ? .green : .gray)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
}
|
||||
}
|
||||
if deviceConnected {
|
||||
Image(systemName: "antenna.radiowaves.left.and.right.circle.fill")
|
||||
.imageScale(.large)
|
||||
.foregroundColor(.green)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text(name).font(name.isEmoji() ? .title : .callout).foregroundColor(.gray)
|
||||
} else {
|
||||
|
||||
Image(systemName: "antenna.radiowaves.left.and.right.slash")
|
||||
.imageScale(.medium)
|
||||
.foregroundColor(.red)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
}
|
||||
} else {
|
||||
Text("bluetooth.off").font(.subheadline).foregroundColor(.red)
|
||||
}
|
||||
}
|
||||
if (phoneOnly && UIDevice.current.userInterfaceIdiom == .phone) || !phoneOnly {
|
||||
if bluetoothOn {
|
||||
if deviceConnected {
|
||||
MQTTIcon(connected: mqttProxyConnected, uplink: mqttUplinkEnabled, downlink: mqttDownlinkEnabled, topic: mqttTopic)
|
||||
Image(systemName: "antenna.radiowaves.left.and.right.circle.fill")
|
||||
.imageScale(.large)
|
||||
.foregroundColor(.green)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text(name).font(name.isEmoji() ? .title : .callout).foregroundColor(.gray)
|
||||
} else {
|
||||
Image(systemName: "antenna.radiowaves.left.and.right.slash")
|
||||
.imageScale(.medium)
|
||||
.foregroundColor(.red)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
}
|
||||
} else {
|
||||
Text("bluetooth.off").font(.subheadline).foregroundColor(.red)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
struct ConnectedDevice_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ConnectedDevice(bluetoothOn: true, deviceConnected: true, name: "MEMO", mqttProxyConnected: true)
|
||||
.previewLayout(.fixed(width: 80, height: 70))
|
||||
|
||||
ConnectedDevice(bluetoothOn: true, deviceConnected: false, name: "86D4", mqttProxyConnected: false)
|
||||
.previewLayout(.fixed(width: 80, height: 70))
|
||||
}
|
||||
|
||||
VStack (alignment: .trailing) {
|
||||
ConnectedDevice(bluetoothOn: true, deviceConnected: true, name: "MEMO", mqttProxyConnected: true)
|
||||
ConnectedDevice(bluetoothOn: true, deviceConnected: true, name: "MEMO", mqttProxyConnected: false, mqttUplinkEnabled: true, mqttDownlinkEnabled: true)
|
||||
ConnectedDevice(bluetoothOn: true, deviceConnected: true, name: "MEMO", mqttProxyConnected: true, mqttUplinkEnabled: true, mqttDownlinkEnabled: true, mqttTopic: "msh/US/2/e/#")
|
||||
ConnectedDevice(bluetoothOn: true, deviceConnected: true, name: "MEMO", mqttProxyConnected: false, mqttUplinkEnabled: true, mqttDownlinkEnabled: false)
|
||||
ConnectedDevice(bluetoothOn: true, deviceConnected: true, name: "MEMO", mqttProxyConnected: true, mqttUplinkEnabled: true, mqttDownlinkEnabled: false)
|
||||
ConnectedDevice(bluetoothOn: true, deviceConnected: true, name: "MEMO", mqttProxyConnected: false, mqttUplinkEnabled: false, mqttDownlinkEnabled: true)
|
||||
ConnectedDevice(bluetoothOn: true, deviceConnected: true, name: "MEMO", mqttProxyConnected: true, mqttUplinkEnabled: false, mqttDownlinkEnabled: true)
|
||||
ConnectedDevice(bluetoothOn: true, deviceConnected: true, name: "MEMO", mqttProxyConnected: true)
|
||||
ConnectedDevice(bluetoothOn: true, deviceConnected: false, name: "MEMO", mqttProxyConnected: false)
|
||||
}.previewLayout(.fixed(width: 150, height: 275))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
54
Meshtastic/Views/Helpers/MQTTIcon.swift
Normal file
54
Meshtastic/Views/Helpers/MQTTIcon.swift
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// MQTTIcon.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Created by Matthew Davies on 4/1/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct MQTTIcon: View {
|
||||
var connected: Bool = false
|
||||
var uplink: Bool = false
|
||||
var downlink: Bool = false
|
||||
var topic: String = ""
|
||||
|
||||
@State var isPopoverOpen = false
|
||||
|
||||
var body: some View {
|
||||
Button( action: {
|
||||
if(topic.length > 0) {self.isPopoverOpen.toggle()}
|
||||
} ) {
|
||||
// the last one defaults to just showing up/down if it isn't specified b/c on the mqtt config screen, there's no information about uplink/downlink and no good alternative icon
|
||||
Image(systemName: uplink && downlink ? "arrow.up.arrow.down.circle.fill" : uplink ? "arrow.up.circle.fill" : downlink ? "arrow.down.circle.fill" : "arrow.up.arrow.down.circle.fill")
|
||||
.imageScale(.large)
|
||||
.foregroundColor(connected ? .green : .gray)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
}.popover(isPresented: self.$isPopoverOpen, content: {
|
||||
VStack(spacing: 0.5) {
|
||||
Text("Subscribed to topic: " + topic)
|
||||
.padding(20)
|
||||
Button("Close", action: { self.isPopoverOpen = false }).padding([.bottom], 20)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct MQTTIcon_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
VStack {
|
||||
MQTTIcon(connected: true)
|
||||
MQTTIcon(connected: false)
|
||||
|
||||
MQTTIcon(connected: true, uplink: true, downlink: true)
|
||||
MQTTIcon(connected: false, uplink: true, downlink: true)
|
||||
|
||||
MQTTIcon(connected: true, uplink: true)
|
||||
MQTTIcon(connected: false, uplink: true)
|
||||
|
||||
MQTTIcon(connected: true, downlink: true)
|
||||
MQTTIcon(connected: false, downlink: true)
|
||||
}.previewLayout(.fixed(width: 25, height: 220))
|
||||
}
|
||||
}
|
||||
|
|
@ -53,11 +53,7 @@ struct WaypointFormMapKit: View {
|
|||
let totalBytes = name.utf8.count
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 30 {
|
||||
let firstNBytes = Data(name.utf8.prefix(30))
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
// Set the name back to the last place where it was the right size
|
||||
name = maxBytesString
|
||||
}
|
||||
name = String(name.dropLast())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -74,11 +70,7 @@ struct WaypointFormMapKit: View {
|
|||
let totalBytes = description.utf8.count
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 100 {
|
||||
let firstNBytes = Data(description.utf8.prefix(100))
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
// Set the name back to the last place where it was the right size
|
||||
description = maxBytesString
|
||||
}
|
||||
description = String(description.dropLast())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ struct ChannelList: View {
|
|||
|
||||
@State private var isPresentingTraceRouteSentAlert = false
|
||||
|
||||
var restrictedChannels = ["admin", "gpio", "mqtt", "serial"]
|
||||
var restrictedChannels = ["gpio", "mqtt", "serial"]
|
||||
|
||||
var body: some View {
|
||||
let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMdd", options: 0, locale: Locale.current)
|
||||
|
|
|
|||
|
|
@ -157,8 +157,12 @@ struct ChannelMessageList: View {
|
|||
bluetoothOn: bleManager.isSwitchedOn,
|
||||
deviceConnected: bleManager.connectedPeripheral != nil,
|
||||
name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?",
|
||||
mqttProxyEnabled: channel.uplinkEnabled || channel.downlinkEnabled,
|
||||
mqttProxyConnected: channel.uplinkEnabled || channel.downlinkEnabled ? bleManager.mqttProxyConnected : false
|
||||
|
||||
// mqttProxyConnected defaults to false, so if it's not enabled it will still be false
|
||||
mqttProxyConnected: bleManager.mqttProxyConnected && (channel.uplinkEnabled || channel.downlinkEnabled),
|
||||
mqttUplinkEnabled: channel.uplinkEnabled,
|
||||
mqttDownlinkEnabled: channel.downlinkEnabled,
|
||||
mqttTopic: bleManager.mqttManager.topic
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,13 +33,7 @@ struct TextMessageField: View {
|
|||
totalBytes = value.utf8.count
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > Self.maxbytes {
|
||||
let firstNBytes = Data(typingMessage.utf8.prefix(Self.maxbytes))
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
// Set the message back to the last place where it was the right size
|
||||
typingMessage = maxBytesString
|
||||
} else {
|
||||
print("not a valid UTF-8 sequence")
|
||||
}
|
||||
typingMessage = String(typingMessage.dropLast())
|
||||
}
|
||||
})
|
||||
.keyboardType(.default)
|
||||
|
|
|
|||
|
|
@ -63,11 +63,7 @@ struct WaypointForm: View {
|
|||
let totalBytes = name.utf8.count
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 30 {
|
||||
let firstNBytes = Data(name.utf8.prefix(30))
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
// Set the name back to the last place where it was the right size
|
||||
name = maxBytesString
|
||||
}
|
||||
name = String(name.dropLast())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -84,11 +80,7 @@ struct WaypointForm: View {
|
|||
let totalBytes = description.utf8.count
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 100 {
|
||||
let firstNBytes = Data(description.utf8.prefix(100))
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
// Set the name back to the last place where it was the right size
|
||||
description = maxBytesString
|
||||
}
|
||||
description = String(description.dropLast())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -203,8 +195,8 @@ struct WaypointForm: View {
|
|||
newWaypoint.id = UInt32(waypoint.id)
|
||||
newWaypoint.name = name.count > 0 ? name : "Dropped Pin"
|
||||
newWaypoint.description_p = description
|
||||
newWaypoint.latitudeI = waypoint.longitudeI
|
||||
newWaypoint.longitudeI = waypoint.latitudeI
|
||||
newWaypoint.latitudeI = waypoint.latitudeI
|
||||
newWaypoint.longitudeI = waypoint.longitudeI
|
||||
// Unicode scalar value for the icon emoji string
|
||||
let unicodeScalers = icon.unicodeScalars
|
||||
// First element as an UInt32
|
||||
|
|
@ -217,7 +209,7 @@ struct WaypointForm: View {
|
|||
newWaypoint.lockedTo = UInt32(lockedTo)
|
||||
}
|
||||
}
|
||||
newWaypoint.expire = UInt32(expire.timeIntervalSince1970)
|
||||
newWaypoint.expire = UInt32(1)
|
||||
if bleManager.sendWaypoint(waypoint: newWaypoint) {
|
||||
|
||||
bleManager.context!.delete(waypoint)
|
||||
|
|
|
|||
|
|
@ -47,11 +47,7 @@ struct ChannelForm: View {
|
|||
let totalBytes = channelName.utf8.count
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 11 {
|
||||
let firstNBytes = Data(channelName.utf8.prefix(11))
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
// Set the channelName back to the last place where it was the right size
|
||||
channelName = maxBytesString
|
||||
}
|
||||
channelName = String(channelName.dropLast())
|
||||
}
|
||||
hasChanges = true
|
||||
})
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ struct DeviceConfig: View {
|
|||
@State var nodeInfoBroadcastSecs = 10800
|
||||
@State var doubleTapAsButtonPress = false
|
||||
@State var isManaged = false
|
||||
@State var tzdef = ""
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
|
|
@ -86,6 +87,26 @@ struct DeviceConfig: View {
|
|||
Label("Debug Log", systemImage: "ant.fill")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
Label("Time Zone", systemImage: "clock.badge.exclamationmark")
|
||||
TextField("Time Zone", text: $tzdef)
|
||||
.foregroundColor(.gray)
|
||||
.onChange(of: tzdef, perform: { _ in
|
||||
let totalBytes = tzdef.utf8.count
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 63 {
|
||||
tzdef = String(tzdef.dropLast())
|
||||
}
|
||||
})
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
.keyboardType(.default)
|
||||
.disableAutocorrection(true)
|
||||
Text("Time zone for dates on the device screen and log.")
|
||||
.foregroundColor(.gray)
|
||||
.font(.callout)
|
||||
}
|
||||
}
|
||||
Section(header: Text("GPIO")) {
|
||||
Picker("Button GPIO", selection: $buttonGPIO) {
|
||||
|
|
@ -179,6 +200,7 @@ struct DeviceConfig: View {
|
|||
dc.nodeInfoBroadcastSecs = UInt32(nodeInfoBroadcastSecs)
|
||||
dc.doubleTapAsButtonPress = doubleTapAsButtonPress
|
||||
dc.isManaged = isManaged
|
||||
dc.tzdef = tzdef
|
||||
if isManaged {
|
||||
serialEnabled = false
|
||||
debugLogEnabled = false
|
||||
|
|
@ -259,6 +281,11 @@ struct DeviceConfig: View {
|
|||
if newIsManaged != node!.deviceConfig!.isManaged { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: tzdef) { newTzdef in
|
||||
if node != nil && node?.deviceConfig != nil {
|
||||
if newTzdef != node!.deviceConfig!.tzdef { hasChanges = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
func setDeviceValues() {
|
||||
self.deviceRole = Int(node?.deviceConfig?.role ?? 0)
|
||||
|
|
@ -273,6 +300,11 @@ struct DeviceConfig: View {
|
|||
}
|
||||
self.doubleTapAsButtonPress = node?.deviceConfig?.doubleTapAsButtonPress ?? false
|
||||
self.isManaged = node?.deviceConfig?.isManaged ?? false
|
||||
self.hasChanges = false
|
||||
if self.tzdef.isEmpty {
|
||||
self.tzdef = TimeZone.current.posixDescription
|
||||
self.hasChanges = true
|
||||
} else {
|
||||
self.hasChanges = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,12 +74,7 @@ struct CannedMessagesConfig: View {
|
|||
let totalBytes = messages.utf8.count
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 198 {
|
||||
|
||||
let firstNBytes = Data(messages.utf8.prefix(198))
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
// Set the shortName back to the last place where it was the right size
|
||||
messages = maxBytesString
|
||||
}
|
||||
messages = String(messages.dropLast())
|
||||
}
|
||||
hasMessagesChanges = true
|
||||
})
|
||||
|
|
|
|||
|
|
@ -94,12 +94,7 @@ struct DetectionSensorConfig: View {
|
|||
let totalBytes = name.utf8.count
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 20 {
|
||||
|
||||
let firstNBytes = Data(name.utf8.prefix(20))
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
// Set the shortName back to the last place where it was the right size
|
||||
name = maxBytesString
|
||||
}
|
||||
name = String(name.dropLast())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,12 @@ struct MQTTConfig: View {
|
|||
if enabled && proxyToClientEnabled && node!.mqttConfig!.proxyToClientEnabled == true {
|
||||
Toggle(isOn: $mqttConnected) {
|
||||
Label(mqttConnected ? "mqtt.disconnect".localized : "mqtt.connect".localized, systemImage: "server.rack")
|
||||
if bleManager.mqttError.count > 0 {
|
||||
Text(bleManager.mqttError)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.foregroundColor(.red)
|
||||
}
|
||||
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
}
|
||||
|
|
@ -139,11 +145,7 @@ struct MQTTConfig: View {
|
|||
let totalBytes = root.utf8.count
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 30 {
|
||||
let firstNBytes = Data(root.utf8.prefix(30))
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
// Set the shortName back to the last place where it was the right size
|
||||
root = maxBytesString
|
||||
}
|
||||
root = String(root.dropLast())
|
||||
}
|
||||
})
|
||||
.foregroundColor(.gray)
|
||||
|
|
@ -181,11 +183,7 @@ struct MQTTConfig: View {
|
|||
let totalBytes = address.utf8.count
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 62 {
|
||||
let firstNBytes = Data(username.utf8.prefix(62))
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
// Set the shortName back to the last place where it was the right size
|
||||
address = maxBytesString
|
||||
}
|
||||
address = String(address.dropLast())
|
||||
}
|
||||
hasChanges = true
|
||||
})
|
||||
|
|
@ -205,14 +203,7 @@ struct MQTTConfig: View {
|
|||
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 62 {
|
||||
|
||||
let firstNBytes = Data(username.utf8.prefix(62))
|
||||
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
|
||||
// Set the shortName back to the last place where it was the right size
|
||||
username = maxBytesString
|
||||
}
|
||||
username = String(username.dropLast())
|
||||
}
|
||||
hasChanges = true
|
||||
})
|
||||
|
|
@ -229,17 +220,9 @@ struct MQTTConfig: View {
|
|||
.onChange(of: password, perform: { _ in
|
||||
|
||||
let totalBytes = password.utf8.count
|
||||
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 62 {
|
||||
|
||||
let firstNBytes = Data(password.utf8.prefix(62))
|
||||
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
|
||||
// Set the shortName back to the last place where it was the right size
|
||||
password = maxBytesString
|
||||
}
|
||||
password = String(password.dropLast())
|
||||
}
|
||||
hasChanges = true
|
||||
})
|
||||
|
|
@ -289,7 +272,7 @@ struct MQTTConfig: View {
|
|||
.navigationTitle("mqtt.config")
|
||||
.navigationBarItems(trailing:
|
||||
ZStack {
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?", mqttProxyEnabled: self.enabled, mqttProxyConnected: bleManager.mqttProxyConnected)
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?", mqttProxyConnected: bleManager.mqttProxyConnected)
|
||||
})
|
||||
.onChange(of: address) { newAddress in
|
||||
if node != nil && node?.mqttConfig != nil {
|
||||
|
|
|
|||
|
|
@ -35,12 +35,7 @@ struct RtttlConfig: View {
|
|||
let totalBytes = ringtone.utf8.count
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 228 {
|
||||
|
||||
let firstNBytes = Data(ringtone.utf8.prefix(228))
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
// Set the ringtone back to the last place where it was the right size
|
||||
ringtone = maxBytesString
|
||||
}
|
||||
ringtone = String(ringtone.dropLast())
|
||||
}
|
||||
})
|
||||
.foregroundColor(.gray)
|
||||
|
|
|
|||
|
|
@ -48,11 +48,7 @@ struct NetworkConfig: View {
|
|||
let totalBytes = wifiSsid.utf8.count
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 32 {
|
||||
let firstNBytes = Data(wifiSsid.utf8.prefix(32))
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
// Set the shortName back to the last place where it was the right size
|
||||
wifiSsid = maxBytesString
|
||||
}
|
||||
wifiSsid = String(wifiSsid.dropLast())
|
||||
}
|
||||
hasChanges = true
|
||||
})
|
||||
|
|
@ -69,11 +65,7 @@ struct NetworkConfig: View {
|
|||
let totalBytes = wifiPsk.utf8.count
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 63 {
|
||||
let firstNBytes = Data(wifiPsk.utf8.prefix(63))
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
// Set the shortName back to the last place where it was the right size
|
||||
wifiPsk = maxBytesString
|
||||
}
|
||||
wifiPsk = String(wifiPsk.dropLast())
|
||||
}
|
||||
hasChanges = true
|
||||
})
|
||||
|
|
|
|||
|
|
@ -54,9 +54,9 @@ struct ShareChannels: View {
|
|||
var body: some View {
|
||||
|
||||
if #available(iOS 17.0, macOS 14.0, *) {
|
||||
// VStack {
|
||||
// TipView(ShareChannelsTip(), arrowEdge: .bottom)
|
||||
// }
|
||||
VStack {
|
||||
TipView(ShareChannelsTip(), arrowEdge: .bottom)
|
||||
}
|
||||
}
|
||||
GeometryReader { bounds in
|
||||
let smallest = min(bounds.size.width, bounds.size.height)
|
||||
|
|
@ -219,10 +219,10 @@ struct ShareChannels: View {
|
|||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(
|
||||
minWidth: smallest * (UIDevice.current.userInterfaceIdiom == .phone ? 0.9 : 0.6),
|
||||
maxWidth: smallest * (UIDevice.current.userInterfaceIdiom == .phone ? 0.9 : 0.6),
|
||||
minHeight: smallest * (UIDevice.current.userInterfaceIdiom == .phone ? 0.9 : 0.6),
|
||||
maxHeight: smallest * (UIDevice.current.userInterfaceIdiom == .phone ? 0.9 : 0.6),
|
||||
minWidth: smallest * (UIDevice.current.userInterfaceIdiom == .phone ? 0.8 : 0.6),
|
||||
maxWidth: smallest * (UIDevice.current.userInterfaceIdiom == .phone ? 0.8 : 0.6),
|
||||
minHeight: smallest * (UIDevice.current.userInterfaceIdiom == .phone ? 0.8 : 0.6),
|
||||
maxHeight: smallest * (UIDevice.current.userInterfaceIdiom == .phone ? 0.8 : 0.6),
|
||||
alignment: .top
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,11 +52,7 @@ struct UserConfig: View {
|
|||
let totalBytes = longName.utf8.count
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > (isLicensed ? 6 : 36) {
|
||||
let firstNBytes = Data(longName.utf8.prefix(isLicensed ? 6 : 36))
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
// Set the longName back to the last place where it was the right size
|
||||
longName = maxBytesString
|
||||
}
|
||||
longName = String(longName.dropLast())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -80,11 +76,7 @@ struct UserConfig: View {
|
|||
let totalBytes = shortName.utf8.count
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 4 {
|
||||
let firstNBytes = Data(shortName.utf8.prefix(4))
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
// Set the shortName back to the last place where it was the right size
|
||||
shortName = maxBytesString
|
||||
}
|
||||
shortName = String(shortName.dropLast())
|
||||
}
|
||||
})
|
||||
.foregroundColor(.gray)
|
||||
|
|
|
|||
|
|
@ -341,7 +341,7 @@
|
|||
"tip.channels.create.title"="Manage Channels";
|
||||
"tip.channels.create.message"="Most data on your mesh is sent over the primary channel. You can set up secondary channels to create additional messaging groups secured by their own key. [Channel config tips](https://meshtastic.org/docs/configuration/tips/)";
|
||||
"tip.channels.share.title"="Sharing Meshtastic Channels";
|
||||
"tip.channels.share.message"="A Meshtastic QR code contains the LoRa config and channel values needed to communicate. Most mesh activity takes place on the required Primary channel. If you don't share your primary channel your first shared channel becomes the primary channel on the other network. Other channels are for private groups, each with its own key.";
|
||||
"tip.channels.share.message"="A Meshtastic QR code contains the LoRa config and channel values needed for radios to communicate. You can share a complete channel configuration using the Replace Channels option, if you choose Add Channels your shared channels will be added to the channels on the receiving radio.";
|
||||
"tip.messages.title"="Messages";
|
||||
"tip.messages.message"="You can send and receive channel (group chats) and direct messages. From any message you can long press to see available actions like copy, reply, tapback and delete as well as delivery details.";
|
||||
"twitter"="Twitter";
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit e6b4c590e7c489306c9c44e3ad1fcf62a3efd288
|
||||
Subproject commit 68720ed8dbcb2c055e3d1ecd4f78d60692f59493
|
||||
356
se.lproj/Localizable.strings
Normal file
356
se.lproj/Localizable.strings
Normal file
|
|
@ -0,0 +1,356 @@
|
|||
/*
|
||||
Localizable.strings
|
||||
Meshtastic
|
||||
|
||||
Copyright(c) Garth Vander Houwen on 12/12/22.
|
||||
|
||||
*/
|
||||
"about"="Om";
|
||||
"about.meshtastic"="Om Meshtastic";
|
||||
"admin"="Administratör";
|
||||
"admin.log"="Administratörsmeddelandelogg";
|
||||
"ago"="sedan";
|
||||
"airtime"="Sändningstid";
|
||||
"always.on"="Alltid på";
|
||||
"ambient.lighting"="Omgivningsbelysning";
|
||||
"ambient.lighting.config"="Konfiguration av omgivningsbelysning";
|
||||
"appsettings"="Appinställningar";
|
||||
"appsettings.provide.location"="Dela plats";
|
||||
"appsettings.smartposition"="Smart position";
|
||||
"are.you.sure"="Är du säker?";
|
||||
"ascii.capable"="ASCII-kompatibel";
|
||||
"available.radios"="Tillgängliga radioapparater";
|
||||
"automatic.detection"="Automatisk upptäckt";
|
||||
"battery.level"="Batterinivå";
|
||||
"ble.name"="BLE-namn";
|
||||
"ble.connection.timeout %d %@"="Anslutningen misslyckades efter %d försök att ansluta till %@. Du kan behöva glömma din enhet under Inställningar > Bluetooth.";
|
||||
"ble.errorcode.6 %@"="%@ Appen kommer automatiskt att återansluta till den föredragna radion om den kommer inom räckhåll igen.";
|
||||
"ble.errorcode.14 %@"="%@ Detta fel kan vanligtvis inte åtgärdas utan att glömma enheten under Inställningar > Bluetooth och återansluta till radion.";
|
||||
"ble.errorcode.pin %@"="%@ Försök att ansluta igen och kontrollera PIN-koden noggrant.";
|
||||
"bluetooth"="Bluetooth";
|
||||
"bluetooth.off"="Bluetooth är avstängt";
|
||||
"bluetooth.config"="Bluetooth-konfiguration";
|
||||
"bluetooth.mode.randompin"="Slumpmässig PIN";
|
||||
"bluetooth.mode.fixedpin"="Fast PIN";
|
||||
"bluetooth.mode.nopin"="Ingen PIN (Bara fungerar)";
|
||||
"bluetooth.pairingmode"="Parläge";
|
||||
"bluetooth.pin.validation"="BLE-PIN måste vara 6 siffror lång.";
|
||||
"bytes"="Bytes";
|
||||
"cancel"="Avbryt";
|
||||
"canned.messages"="Fördefinierade meddelanden";
|
||||
"canned.messages.config"="Konfiguration av fördefinierade meddelanden";
|
||||
"canned.messages.preset.manual"="Manuell konfiguration";
|
||||
"canned.messages.preset.rakrotary"="RAK Rotary Encoder-modul";
|
||||
"canned.messages.preset.cardkb"="M5 Stack Card KB / RAK Keypad";
|
||||
"channel"="Kanal";
|
||||
"channel.role.disabled"="Inaktiverad";
|
||||
"channel.role.primary"="Primär";
|
||||
"channel.role.secondary"="Sekundär";
|
||||
"channel.utilization"="Kanalutnyttjande";
|
||||
"channels"="Kanaler";
|
||||
"clear.app.data"="Rensa appdata";
|
||||
"clear.log"="Rensa";
|
||||
"close"="Stäng";
|
||||
"config.power.settings"="Ström";
|
||||
"config.power.title"="Strömkonfiguration";
|
||||
"config.power.section.battery"="Batteri";
|
||||
"config.power.section.sleep"="Sömn";
|
||||
"config.power.adc.override"="ADC-överskrivning";
|
||||
"config.power.adc.multiplier"="Multiplikator";
|
||||
"config.power.ls.secs"="Intervall för Ljussömn";
|
||||
"config.power.min.wake.secs"="Minsta Väckningsintervall";
|
||||
"config.power.saving"="Strömsparläge";
|
||||
"config.power.saving.description"="Sätter allt i viloläge så mycket som möjligt, för spårnings- och sensorläge kommer detta också inkludera LoRa-radion. Använd inte denna inställning om du vill använda din enhet med mobilappar eller använder en enhet utan en användarknapp.";
|
||||
"config.power.shutdown.on.power.loss"="Stäng av vid Strömförlust";
|
||||
"config.power.shutdown.after.secs"="Efter";
|
||||
"config.power.wait.bluetooth.secs"="Bluetooth Stängs Av Efter";
|
||||
"config.ringtone"="RTTTL Ringsignal";
|
||||
"config.ringtone.title"="Ringsignalskonfiguration";
|
||||
"config.ringtone.label"="Språk för Överföring av Ringsignal";
|
||||
"config.ringtone.description"="Ringsignalöverföringsspråk (RTTTL) Ringsignalsträng som används av stödda buzzers i externa notifikationer.";
|
||||
"config.module.paxcounter.settings"="PAX Räknare";
|
||||
"config.module.paxcounter.title"="PAX Räknare Konfiguration";
|
||||
"config.module.paxcounter.enabled.description"="När aktiverad räknar PAX-räknarmodulen antalet personer som passerar med WiFi och Bluetooth. Både WiFi och Bluetooth måste vara aktiverade för att PAX-räknaren ska fungera.";
|
||||
"config.module.paxcounter.updateinterval"="Uppdateringsintervall";
|
||||
"config.module.paxcounter.updateinterval.description"="Hur ofta vi kan skicka ett meddelande till mesh-nätverket när personer upptäcks.";
|
||||
"config.save.confirm"="Efter att konfigurationsvärdena sparats kommer noden att starta om.";
|
||||
"communicating"="Kommunicerar med enheten...";
|
||||
"connected.radio"="Ansluten Radio";
|
||||
"connected"="Bluetooth Ansluten";
|
||||
"connecting"="Ansluter...";
|
||||
"contacts"="Kontakter";
|
||||
"contacts %@"="Kontakter (%@)";
|
||||
"copy"="Kopiera";
|
||||
"current"="Aktuell";
|
||||
"default"="Standard";
|
||||
"delete"="Ta bort";
|
||||
"detection.sensor"="Detektionssensor";
|
||||
"detection.sensor.config"="Konfiguration av Detektionssensor";
|
||||
"detection.sensor.log"="Logg för Detektionssensor";
|
||||
"device"="Enhet";
|
||||
"device.config"="Enhetskonfiguration";
|
||||
"device.configuration"="Enhetsinställningar";
|
||||
"device.metrics.delete"="Ta bort alla enhetsmätvärden?";
|
||||
"device.metrics.log"="Logg för Enhetsmätvärden";
|
||||
"device.role.client"="Appansluten eller fristående meddelandeenhet.";
|
||||
"device.role.clientmute"="Enhet som inte vidarebefordrar paket från andra enheter.";
|
||||
"device.role.clienthidden"="Enhet som endast sänder ut när det behövs för stealth eller energibesparing.";
|
||||
"device.role.tracker"="Sänder ut GPS-positionspaket som prioritet.";
|
||||
"device.role.lostandfound"="Sänder regelbundet ut plats som meddelande till standardkanalen för att underlätta återhämtning av enheten.";
|
||||
"device.role.sensor"="Sänder ut telemetripaket som prioritet.";
|
||||
"device.role.tak"="Optimerad för kommunikation med ATAK-systemet, minskar rutinutsändningar.";
|
||||
"device.role.taktracker"="Aktiverar automatiska TAK PLI-utsändningar och minskar rutinutsändningar.";
|
||||
"device.role.repeater"="Infrastrukturnod för att utöka nätverkstäckningen genom att vidarebefordra meddelanden med minimal overhead. Syns inte i Noder-listan.";
|
||||
"device.role.router"="Infrastrukturnod för att utöka nätverkstäckningen genom att vidarebefordra meddelanden. Synlig i Noder-listan.";
|
||||
"device.role.routerclient"="Kombination av både ROUTER och CLIENT. Inte för mobila enheter.";
|
||||
"direct.messages"="Direktmeddelanden";
|
||||
"dismiss.keyboard"="Stäng";
|
||||
"display"="Skärm";
|
||||
"display.config"="Skärmkonfiguration";
|
||||
"distance"="Distans";
|
||||
"disconnect"="Koppla från";
|
||||
"echo"="Eko";
|
||||
"email.address"="E-postadress";
|
||||
"enabled"="Aktiverad";
|
||||
"encrypted"="Krypterad";
|
||||
"external.notification"="Extern Notifikation";
|
||||
"external.notification.config"="Konfiguration av Extern Notifikation";
|
||||
"finish"="Avsluta";
|
||||
"firmware.version"="Firmwareversion";
|
||||
"firmware.version.unsupported"="Okänd Firmwareversion upptäckt, kan inte ansluta till enheten.";
|
||||
"gas"="Gas";
|
||||
"gas.resistance"="Gasmotstånd";
|
||||
"generate.qr.code"="Generera QR-kod";
|
||||
"gpsformat.dec"="Decimalgrader";
|
||||
"gpsformat.dms"="Grader Minuter Sekunder";
|
||||
"gpsformat.utm"="Universal Transversal Mercator";
|
||||
"gpsformat.mgrs"="Militärt rutnätsreferenssystem";
|
||||
"gpsformat.olc"="Öppen Platskod (även känd som Pluskoder)";
|
||||
"gpsformat.osgr"="Ordnance Survey Rutnätsreferens";
|
||||
"gpsmode.disabled"="Inaktiverad";
|
||||
"gpsmode.enabled"="Aktiverad";
|
||||
"gpsmode.notPresent"="Inte närvarande";
|
||||
"heard"="Hörd";
|
||||
"heard.last"="Senast Hörd";
|
||||
"hybrid"="Hybrid";
|
||||
"hybrid.flyover"="Hybrid Flygöversikt";
|
||||
"include"="Inkludera";
|
||||
"inputevent.none"="Ingen";
|
||||
"inputevent.up"="Upp";
|
||||
"inputevent.down"="Ner";
|
||||
"inputevent.left"="Vänster";
|
||||
"inputevent.right"="Höger";
|
||||
"inputevent.select"="Välj";
|
||||
"inputevent.back"="Bakåt";
|
||||
"inputevent.cancel"="Avbryt";
|
||||
"interval.one.second"="En Sekund";
|
||||
"interval.two.seconds"="Två Sekunder";
|
||||
"interval.three.seconds"="Tre Sekunder";
|
||||
"interval.four.seconds"="Fyra Sekunder";
|
||||
"interval.five.seconds"="Fem Sekunder";
|
||||
"interval.ten.seconds"="Tio Sekunder";
|
||||
"interval.fifteen.seconds"="Femton Sekunder";
|
||||
"interval.twenty.seconds"="Tjugo Sekunder";
|
||||
"interval.twentyfive.seconds"="Tjugofem Sekunder";
|
||||
"interval.thirty.seconds"="Trettio Sekunder";
|
||||
"interval.fortyfive.seconds"="Fyrtiofem Sekunder";
|
||||
"interval.one.minute"="En Minut";
|
||||
"interval.two.minutes"="Två Minuter";
|
||||
"interval.five.minutes"="Fem Minuter";
|
||||
"interval.ten.minutes"="Tio Minuter";
|
||||
"interval.fifteen.minutes"="Femton Minuter";
|
||||
"interval.thirty.minutes"="Trettio Minuter";
|
||||
"interval.one.hour"="En Timme";
|
||||
"interval.two.hours"="Två Timmar";
|
||||
"interval.three.hours"="Tre Timmar";
|
||||
"interval.four.hours"="Fyra Timmar";
|
||||
"interval.five.hours"="Fem Timmar";
|
||||
"interval.six.hours"="Sex Timmar";
|
||||
"interval.twelve.hours"="Tolv Timmar";
|
||||
"interval.eighteen.hours"="Arton Timmar";
|
||||
"interval.twentyfour.hours"="Tjugofyra Timmar";
|
||||
"interval.thirtysix.hours"="Trettiosex Timmar";
|
||||
"interval.fortyeight.hours"="Fyrtioåtta Timmar";
|
||||
"interval.seventytwo.hours"="Sjuttiotvå Timmar";
|
||||
"keyboard.type"="Tangentbordstyp";
|
||||
"logging"="Loggning";
|
||||
"lora"="LoRa";
|
||||
"lora.config"="LoRa Konfiguration";
|
||||
"map"="Mesh Karta";
|
||||
"map.type"="Standardtyp";
|
||||
"map.centering"="Centreringsläge";
|
||||
"map.tiles.delete"="Radera Alla Kartplattor";
|
||||
"map.recentering"="Automatisk Centrering";
|
||||
"map.use.legacy"="Använd Äldre Mesh Karta";
|
||||
"map.usertrackingmode"="Spårningsläge för användare";
|
||||
"map.usertrackingmode.follow"="Följ";
|
||||
"map.usertrackingmode.followwithheading"="Följ med riktning";
|
||||
"map.usertrackingmode.none"="Ingen";
|
||||
"mesh.live.activity"="Mesh Live Aktivitet";
|
||||
"mesh.log"="Mesh-logg";
|
||||
"mesh.log.ambientlighting.config %@"="Konfiguration för omgivningsbelysningsmodulen mottagen: %@";
|
||||
"mesh.log.bluetooth.config %@"="Bluetooth-konfiguration mottagen: %@";
|
||||
"mesh.log.cannedmessage.config %@"="Konfiguration för modulen med fördefinierade meddelanden mottagen: %@";
|
||||
"mesh.log.cannedmessages.messages.get %@"="Begärda meddelanden för modulen med fördefinierade meddelanden för nod: %@";
|
||||
"mesh.log.cannedmessages.messages.received %@"="Mottagna meddelanden för fördefinierade meddelanden För: %@";
|
||||
"mesh.log.channel.sent %@ %d"="Skickade en kanal för: %@ Kanalindex %d";
|
||||
"mesh.log.channel.received %d %@"="Kanal %d mottagen från: %@";
|
||||
"mesh.log.device.config %@"="Enhetskonfiguration mottagen: %@";
|
||||
"mesh.log.display.config %@"="Skärmkonfiguration mottagen: %@";
|
||||
"mesh.log.devicemetadata %@"="Begär metadata för enhet för %@";
|
||||
"mesh.log.device.metadata.received %@"="Metadata för enhet mottagen från: %@";
|
||||
"mesh.log.detectionsensor.config %@"="Konfiguration för detektionssensormodulen mottagen: %@";
|
||||
"mesh.log.externalnotification.config %@"="Konfiguration för modulen för externa notifikationer mottagen: %@";
|
||||
"mesh.log.lora.config %@"="LoRa-konfiguration mottagen: %@";
|
||||
"mesh.log.lora.config.sent %@"="Skickade en LoRa.Konfiguration för: %@";
|
||||
"mesh.log.mqtt.config %@"="MQTT-modulkonfiguration mottagen: %@";
|
||||
"mesh.log.myinfo %@"="Min info mottagen: %@";
|
||||
"mesh.log.network.config %@"="Nätverkskonfiguration mottagen: %@";
|
||||
"mesh.log.nodeinfo.received %@"="Nodinformation mottagen för: %@";
|
||||
"mesh.log.paxcounter %@"="PAX-räknarmeddelande mottaget från: %@";
|
||||
"mesh.log.paxcounter.config %@"="PAX-räknarkonfiguration mottagen: %@";
|
||||
"mesh.log.position.config %@"="Positionskonfiguration mottagen: %@";
|
||||
"mesh.log.position.received %@"="Positionspaket mottaget från nod: %@";
|
||||
"mesh.log.power.config %@"="Strömkonfiguration mottagen: %@";
|
||||
"mesh.log.rangetest.config %@"="Konfiguration för räckviddstestmodulen mottagen: %@";
|
||||
"mesh.log.ringtone.config %@"="Konfiguration för RTTTL-ringsignal mottagen: %@";
|
||||
"mesh.log.routing.message %@ %@"="Routing mottagen för RequestID: %@ Ack Status: %@";
|
||||
"mesh.log.serial.config %@"="Seriekonfigurationsmodul mottagen: %@";
|
||||
"mesh.log.sharelocation %@"="Skickade ett positionspaket från Apple-enhetens GPS till nod: %@";
|
||||
"mesh.log.storeforward.config %@"="Konfiguration för Store & Forward-modulen mottagen: %@";
|
||||
"mesh.log.telemetry.config %@"="Telemetrimodulkonfiguration mottagen: %@";
|
||||
"mesh.log.telemetry.received %@"="Telemetri mottagen för: %@";
|
||||
"mesh.log.textmessage.received"="Meddelande mottaget från textmeddelandeappen.";
|
||||
"mesh.log.textmessage.send.failed %@"="Misslyckades med att skicka meddelande, inte korrekt ansluten till %@";
|
||||
"mesh.log.textmessage.sent %@ %@ %@"="Skickade meddelande %@ från %@ till %@";
|
||||
"mesh.log.traceroute.received.direct %@"="Spårruttförfrågan skickad till nod: %@ mottogs direkt.";
|
||||
"mesh.log.traceroute.received.route %@"="Spårruttförfrågan returnerade: %@";
|
||||
"mesh.log.traceroute.sent %@"="Skickade en spårruttförfrågan till nod: %@";
|
||||
"mesh.log.wantconfig %@"="Utfärdar Want Config till %@";
|
||||
"mesh.log.waypoint.sent %@"="Skickade en vägpunktspaket från: %@";
|
||||
"mesh.log.waypoint.received %@"="Vägpunktspaket mottaget från nod: %@";
|
||||
"message"="Meddelande";
|
||||
"message.details"="Meddelandedetaljer";
|
||||
"messages"="Meddelanden";
|
||||
"mode"="Läge";
|
||||
"module.configuration"="Modulkonfiguration";
|
||||
"mqtt"="MQTT";
|
||||
"mqtt.connect"="Anslut till MQTT";
|
||||
"mqtt.config"="MQTT-konfiguration";
|
||||
"mqtt.clientproxy"="MQTT-klientproxy";
|
||||
"mqtt.disconnect"="Koppla från MQTT";
|
||||
"mqtt.username"="Användarnamn";
|
||||
"name"="Namn";
|
||||
"network"="Nätverk";
|
||||
"network.config"="Nätverkskonfiguration";
|
||||
"nodes"="Noder";
|
||||
"nodes %@"="Noder (%@)";
|
||||
"nodelist.filter.distance %@"="upp till %@ bort";
|
||||
"save.config %@"="Spara konfiguration för %@";
|
||||
"no.nodes"="Inga Meshtastic-noder hittades";
|
||||
"not.connected"="Ingen enhet ansluten";
|
||||
"numbers.punctuation"="Siffror och skiljetecken";
|
||||
"off"="Av";
|
||||
"offline"="Offline";
|
||||
"on.boot"="Endast vid uppstart";
|
||||
"options"="Alternativ";
|
||||
"password"="Lösenord";
|
||||
"pause"="Pausa";
|
||||
"paxcounter.ble"="BLE";
|
||||
"paxcounter.delete"="Radera all paxdata?";
|
||||
"paxcounter.wifi"="WiFi";
|
||||
"paxcounter.uptime"="Drifttid";
|
||||
"paxcounter.content.unavailable"="Inga loggar för PAX-räknare";
|
||||
"paxcounter.log"="PAX-räknarens logg";
|
||||
"paxcounter.total"="Totalt PAX";
|
||||
"phone.gps"="Telefon-GPS";
|
||||
"phone.gps.interval.description"="Hur ofta din telefon skickar din plats till enheten, platsuppdateringar till mesh-nätverket hanteras av enheten.";
|
||||
"position"="Position";
|
||||
"position.config"="Positionskonfiguration";
|
||||
"position.precision %@"="Inom %@";
|
||||
"preferred.radio"="Föredragen Radio";
|
||||
"radio.configuration"="Radioinställningar";
|
||||
"range.test"="Räckviddstest";
|
||||
"range.test.blocked"="Blockera räckviddstest";
|
||||
"range.test.config"="Konfiguration av räckviddstest";
|
||||
"reply"="Svara";
|
||||
"reboot"="Starta om";
|
||||
"reboot.node"="Starta om nod?";
|
||||
"received.ack"="Mottaget kvitto";
|
||||
"received.ack.real"="Mottagarkvitto";
|
||||
"resume"="Återuppta";
|
||||
"ringtone"="Ringsignal";
|
||||
"ringtone.config"="Ringsignalsinställningar";
|
||||
"route.recorder"="Ruttinspelare";
|
||||
"routes"="Rutter";
|
||||
"routing.acknowledged"="Bekräftad";
|
||||
"routing.noroute"="Ingen rutt";
|
||||
"routing.gotnak"="Mottog ett negativt kvitto";
|
||||
"routing.timeout"="Tidsgräns överskriden";
|
||||
"routing.nointerface"="Inget gränssnitt";
|
||||
"routing.maxretransmit"="Max antal omsändningar nått";
|
||||
"routing.nochannel"="Ingen kanal";
|
||||
"routing.toolarge"="Paketet är för stort";
|
||||
"routing.noresponse"="Inget svar";
|
||||
"routing.dutycyclelimit"="Regionala sändningsgränsen nådd";
|
||||
"routing.badRequest"="Felaktig begäran";
|
||||
"routing.notauthorized"="Inte auktoriserad";
|
||||
"satellite"="Satellit";
|
||||
"satellite.flyover"="Satellitöverflygning";
|
||||
"save"="Spara";
|
||||
"save.config %@"="Spara konfiguration för %@";
|
||||
"serial"="Serie";
|
||||
"serial.config"="Seriekonfiguration";
|
||||
"serial.mode.default"="Standard";
|
||||
"serial.mode.simple"="Enkel";
|
||||
"serial.mode.proto"="Protobufs";
|
||||
"serial.mode.txtmsg"="Textmeddelande";
|
||||
"serial.mode.nmea"="NMEA-positioner";
|
||||
"settings"="Inställningar";
|
||||
"share.channels"="Dela QR-kod";
|
||||
"share.position"="Dela position";
|
||||
"subscribed"="Prenumererar på mesh";
|
||||
"select.contact"="Välj en kontakt";
|
||||
"select.node"="Välj en nod";
|
||||
"select.menu.item"="Välj ett alternativ från menyn";
|
||||
"set.region"="Ställ in LoRa-region";
|
||||
"standard"="Standard";
|
||||
"standard.muted"="Standard Muted";
|
||||
"start"="Start";
|
||||
"storeforward"="Lagra & Videresänd";
|
||||
"storeforward.config"="Konfiguration för Lagra & Videresänd";
|
||||
"storeforward.heartbeat"="Skicka hjärtslag";
|
||||
"ssid"="SSID";
|
||||
"tapback"="Svarsreaktion";
|
||||
"tapback.heart"="Hjärta";
|
||||
"tapback.thumbsup"="Tummen upp";
|
||||
"tapback.thumbsdown"="Tummen ner";
|
||||
"tapback.haha"="HaHa";
|
||||
"tapback.exclamation"="Utropstecken";
|
||||
"tapback.question"="Frågetecken";
|
||||
"tapback.poop"="Bajs";
|
||||
"tapback.wave"="Vinka";
|
||||
"telemetry"="Telemetri (Sensorer)";
|
||||
"telemetry.config"="Telemetriinställningar";
|
||||
"timeout"="Tidsgräns överskriden";
|
||||
"timestamp"="Tidsstämpel";
|
||||
"tip.bluetooth.connect.title"="Ansluten Radio";
|
||||
"tip.bluetooth.connect.message"="Visar information för LoRa-radion ansluten via bluetooth. Du kan svepa åt vänster för att koppla från radion och långtryck för att visa statistik eller starta liveaktivitet.";
|
||||
"tip.channel.admin.title"="Administratörskanal";
|
||||
"tip.channel.admin.message"="Administratörskanal upptäckt: Välj en nod från rullgardinsmenyn för att hantera anslutna eller fjärranslutna enheter.";
|
||||
"tip.channels.create.title"="Hantera Kanaler";
|
||||
"tip.channels.create.message"="De flesta data i ditt mesh-nätverk skickas över primärkanalen. Du kan ställa in sekundära kanaler för att skapa ytterligare meddelandegrupper skyddade av sin egen nyckel. Tips för kanalkonfiguration";
|
||||
"tip.channels.share.title"="Dela Meshtastic-kanaler";
|
||||
"tip.channels.share.message"="En Meshtastic QR-kod innehåller LoRa-konfigurationen och kanalvärden som behövs för kommunikation. De flesta aktiviteter i mesh-nätverket sker på den obligatoriska primärkanalen. Om du inte delar din primärkanal blir din första delade kanal primärkanalen på det andra nätverket. Andra kanaler är för privata grupper, varje med sin egen nyckel.";
|
||||
"tip.messages.title"="Meddelanden";
|
||||
"tip.messages.message"="Du kan skicka och ta emot kanalmeddelanden (gruppchatt) och direkta meddelanden. Från alla meddelanden kan du långtrycka för att se tillgängliga åtgärder som kopiera, svara, tapback och radera samt leveransdetaljer.";
|
||||
"twitter"="Twitter";
|
||||
"unknown"="Okänd";
|
||||
"unknown.age"="Okänd ålder";
|
||||
"unset"="Återställ";
|
||||
"update.firmware"="Uppdatera din firmware";
|
||||
"update.interval"="Uppdateringsintervall";
|
||||
"user"="Användare";
|
||||
"user.details"="Användaruppgifter";
|
||||
"voltage"="Spänning";
|
||||
"waiting"="Väntar...";
|
||||
Loading…
Add table
Add a link
Reference in a new issue