mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge pull request #148 from meshtastic/ble_config_updates
Ble config updates
This commit is contained in:
commit
95b70a73cb
22 changed files with 1077 additions and 376 deletions
|
|
@ -70,6 +70,14 @@
|
|||
DDAF8C6E26ED19040058C060 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C6D26ED19040058C060 /* Extensions.swift */; };
|
||||
DDB2CC6E27F3EB47009C5FCC /* telemetry.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB2CC6D27F3EB47009C5FCC /* telemetry.pb.swift */; };
|
||||
DDB3107228A6224100F1DE3D /* device_metadata.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB3107128A6224100F1DE3D /* device_metadata.pb.swift */; };
|
||||
DDB6ABD628AE742000384BA1 /* BluetoothConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABD528AE742000384BA1 /* BluetoothConfig.swift */; };
|
||||
DDB6ABD928B0A4BA00384BA1 /* BluetoothModes.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABD828B0A4BA00384BA1 /* BluetoothModes.swift */; };
|
||||
DDB6ABDB28B0AC6000384BA1 /* DistanceText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABDA28B0AC6000384BA1 /* DistanceText.swift */; };
|
||||
DDB6ABE028B13AC700384BA1 /* DeviceRoles.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABDF28B13AC700384BA1 /* DeviceRoles.swift */; };
|
||||
DDB6ABE228B13FB500384BA1 /* GpsFormats.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE128B13FB500384BA1 /* GpsFormats.swift */; };
|
||||
DDB6ABE428B13FFF00384BA1 /* ScreenIntervals.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE328B13FFF00384BA1 /* ScreenIntervals.swift */; };
|
||||
DDB6ABE628B1406100384BA1 /* LoraConfigEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE528B1406100384BA1 /* LoraConfigEnums.swift */; };
|
||||
DDB6ABE828B141AF00384BA1 /* WiFiModes.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE728B141AF00384BA1 /* WiFiModes.swift */; };
|
||||
DDC2E15826CE248E0042C5E4 /* MeshtasticApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E15726CE248E0042C5E4 /* MeshtasticApp.swift */; };
|
||||
DDC2E15C26CE248F0042C5E4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */; };
|
||||
DDC2E15F26CE248F0042C5E4 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15E26CE248F0042C5E4 /* Preview Assets.xcassets */; };
|
||||
|
|
@ -171,6 +179,15 @@
|
|||
DDB2CC6D27F3EB47009C5FCC /* telemetry.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = telemetry.pb.swift; sourceTree = "<group>"; };
|
||||
DDB2CC6F27F3F0AC009C5FCC /* MeshtasticDataModel v 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 3.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DDB3107128A6224100F1DE3D /* device_metadata.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = device_metadata.pb.swift; sourceTree = "<group>"; };
|
||||
DDB6ABD528AE742000384BA1 /* BluetoothConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothConfig.swift; sourceTree = "<group>"; };
|
||||
DDB6ABD728AE8F5D00384BA1 /* MeshtasticDataModel v 7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 7.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DDB6ABD828B0A4BA00384BA1 /* BluetoothModes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothModes.swift; sourceTree = "<group>"; };
|
||||
DDB6ABDA28B0AC6000384BA1 /* DistanceText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DistanceText.swift; sourceTree = "<group>"; };
|
||||
DDB6ABDF28B13AC700384BA1 /* DeviceRoles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRoles.swift; sourceTree = "<group>"; };
|
||||
DDB6ABE128B13FB500384BA1 /* GpsFormats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GpsFormats.swift; sourceTree = "<group>"; };
|
||||
DDB6ABE328B13FFF00384BA1 /* ScreenIntervals.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenIntervals.swift; sourceTree = "<group>"; };
|
||||
DDB6ABE528B1406100384BA1 /* LoraConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoraConfigEnums.swift; sourceTree = "<group>"; };
|
||||
DDB6ABE728B141AF00384BA1 /* WiFiModes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WiFiModes.swift; sourceTree = "<group>"; };
|
||||
DDC2E15426CE248E0042C5E4 /* Meshtastic.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Meshtastic.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DDC2E15726CE248E0042C5E4 /* MeshtasticApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticApp.swift; sourceTree = "<group>"; };
|
||||
DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = ../Assets.xcassets; sourceTree = "<group>"; };
|
||||
|
|
@ -278,6 +295,7 @@
|
|||
DD61937A2863876A00E59241 /* Config */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DDB6ABD528AE742000384BA1 /* BluetoothConfig.swift */,
|
||||
DD41582528582E9B009B0E59 /* DeviceConfig.swift */,
|
||||
DD8EBF42285058FA00426DCA /* DisplayConfig.swift */,
|
||||
DD2553562855B02500E55709 /* LoRaConfig.swift */,
|
||||
|
|
@ -313,7 +331,13 @@
|
|||
DD8ED9C6289CE4A100B3B0AB /* Enums */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DDB6ABD828B0A4BA00384BA1 /* BluetoothModes.swift */,
|
||||
DDB6ABDF28B13AC700384BA1 /* DeviceRoles.swift */,
|
||||
DDB6ABE528B1406100384BA1 /* LoraConfigEnums.swift */,
|
||||
DDB6ABE128B13FB500384BA1 /* GpsFormats.swift */,
|
||||
DD8ED9C7289CE4B900B3B0AB /* RoutingError.swift */,
|
||||
DDB6ABE328B13FFF00384BA1 /* ScreenIntervals.swift */,
|
||||
DDB6ABE728B141AF00384BA1 /* WiFiModes.swift */,
|
||||
);
|
||||
path = Enums;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -466,6 +490,7 @@
|
|||
DDC3B273283F411B00AC321C /* LastHeardText.swift */,
|
||||
DDA6B2EA28420A7B003E8C16 /* NodeAnnotation.swift */,
|
||||
DDD94A4F2845C8F5004A87A0 /* DateTimeText.swift */,
|
||||
DDB6ABDA28B0AC6000384BA1 /* DistanceText.swift */,
|
||||
);
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -662,8 +687,10 @@
|
|||
DD5394FE276BA0EF00AD86B1 /* PositionEntityExtension.swift in Sources */,
|
||||
DD913639270DFF4C00D7ACF3 /* LocalNotificationManager.swift in Sources */,
|
||||
DD4C158C2824A91E0032668E /* module_config.pb.swift in Sources */,
|
||||
DDB6ABE828B141AF00384BA1 /* WiFiModes.swift in Sources */,
|
||||
DD4F23CD28779A3C001D37CB /* TelemetryLog.swift in Sources */,
|
||||
DD6B85A828009258000ACD6B /* ShareChannel.swift in Sources */,
|
||||
DDB6ABD628AE742000384BA1 /* BluetoothConfig.swift in Sources */,
|
||||
DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */,
|
||||
DDC4D568275499A500A4208E /* Persistence.swift in Sources */,
|
||||
DD90860E26F69BAE00DC5189 /* NodeMap.swift in Sources */,
|
||||
|
|
@ -680,8 +707,10 @@
|
|||
DD6193772862F90F00E59241 /* CannedMessagesConfig.swift in Sources */,
|
||||
DD41582628582E9B009B0E59 /* DeviceConfig.swift in Sources */,
|
||||
DD1BF2F92776FE2E008C8D2F /* UserMessageList.swift in Sources */,
|
||||
DDB6ABE628B1406100384BA1 /* LoraConfigEnums.swift in Sources */,
|
||||
DDB2CC6E27F3EB47009C5FCC /* telemetry.pb.swift in Sources */,
|
||||
DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */,
|
||||
DDB6ABDB28B0AC6000384BA1 /* DistanceText.swift in Sources */,
|
||||
C9A7BC1027759A9600760B50 /* PositionAnnotationView.swift in Sources */,
|
||||
DD882F5D2772E4640005BF05 /* Contacts.swift in Sources */,
|
||||
DD47E3CE26F103C600029299 /* NodeList.swift in Sources */,
|
||||
|
|
@ -690,11 +719,13 @@
|
|||
DDC2E18F26CE25FE0042C5E4 /* ContentView.swift in Sources */,
|
||||
DD17E5DE277D49D400010EC2 /* storeforward.pb.swift in Sources */,
|
||||
DD2553572855B02500E55709 /* LoRaConfig.swift in Sources */,
|
||||
DDB6ABD928B0A4BA00384BA1 /* BluetoothModes.swift in Sources */,
|
||||
DDD9E4E4284B208E003777C5 /* UserEntityExtension.swift in Sources */,
|
||||
C9A88B55278B503C00BD810A /* MapViewModule.swift in Sources */,
|
||||
DD2553592855B52700E55709 /* PositionConfig.swift in Sources */,
|
||||
DDB3107228A6224100F1DE3D /* device_metadata.pb.swift in Sources */,
|
||||
DDAF8C6326ED0A230058C060 /* admin.pb.swift in Sources */,
|
||||
DDB6ABE028B13AC700384BA1 /* DeviceRoles.swift in Sources */,
|
||||
DD86D40C287F401000BAEB7A /* SaveChannelQRCode.swift in Sources */,
|
||||
DD8ED9C8289CE4B900B3B0AB /* RoutingError.swift in Sources */,
|
||||
C9483F6D2773017500998F6B /* MapView.swift in Sources */,
|
||||
|
|
@ -708,11 +739,13 @@
|
|||
DDA6B2E928419CF2003E8C16 /* MeshPackets.swift in Sources */,
|
||||
DDCE4E2C2869F92900BE9F8F /* UserConfig.swift in Sources */,
|
||||
DD6193752862F6E600E59241 /* ExternalNotificationConfig.swift in Sources */,
|
||||
DDB6ABE428B13FFF00384BA1 /* ScreenIntervals.swift in Sources */,
|
||||
DD86D40A287F04F100BAEB7A /* InvalidVersion.swift in Sources */,
|
||||
DDD94A502845C8F5004A87A0 /* DateTimeText.swift in Sources */,
|
||||
C9A88B57278B559900BD810A /* apponly.pb.swift in Sources */,
|
||||
DD4C158E2824AA7E0032668E /* config.pb.swift in Sources */,
|
||||
DD47E3D926F3093800029299 /* MessageBubble.swift in Sources */,
|
||||
DDB6ABE228B13FB500384BA1 /* GpsFormats.swift in Sources */,
|
||||
DD415828285859C4009B0E59 /* TelemetryConfig.swift in Sources */,
|
||||
DD73FD1128750779000852D6 /* LocationHistory.swift in Sources */,
|
||||
C9697F9D279336B700250207 /* LocalMBTileOverlay.swift in Sources */,
|
||||
|
|
@ -1102,6 +1135,7 @@
|
|||
DD9D8F2D2764403B00080993 /* Meshtastic.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
DDB6ABD728AE8F5D00384BA1 /* MeshtasticDataModel v 7.xcdatamodel */,
|
||||
DD8ED9C9289EA77E00B3B0AB /* MeshtasticDataModel v 6.xcdatamodel */,
|
||||
DD8ED9C328978D9D00B3B0AB /* MeshtasticDataModel v 5.xcdatamodel */,
|
||||
DD619373285CC7D600E59241 /* MeshtasticDataModel v 4.xcdatamodel */,
|
||||
|
|
@ -1109,7 +1143,7 @@
|
|||
DD45C77427BD4EF80011784F /* MeshtasticDataModel v2.xcdatamodel */,
|
||||
DD9D8F2E2764403B00080993 /* CoreDataSample.xcdatamodel */,
|
||||
);
|
||||
currentVersion = DD8ED9C9289EA77E00B3B0AB /* MeshtasticDataModel v 6.xcdatamodel */;
|
||||
currentVersion = DDB6ABD728AE8F5D00384BA1 /* MeshtasticDataModel v 7.xcdatamodel */;
|
||||
name = Meshtastic.xcdatamodeld;
|
||||
path = Meshtastic/Meshtastic.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
|
|
|
|||
39
Meshtastic/Enums/BluetoothModes.swift
Normal file
39
Meshtastic/Enums/BluetoothModes.swift
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// BluetoothModes.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright(c) Garth Vander Houwen 8/19/22.
|
||||
//
|
||||
|
||||
enum BluetoothModes: Int, CaseIterable, Identifiable {
|
||||
|
||||
case randomPin = 0
|
||||
case fixedPin = 1
|
||||
case noPin = 2
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .randomPin:
|
||||
return "Random PIN"
|
||||
case .fixedPin:
|
||||
return "Fixed PIN"
|
||||
case .noPin:
|
||||
return "No PIN (Just Works)"
|
||||
}
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Config.BluetoothConfig.PairingMode {
|
||||
|
||||
switch self {
|
||||
|
||||
case .randomPin:
|
||||
return Config.BluetoothConfig.PairingMode.randomPin
|
||||
case .fixedPin:
|
||||
return Config.BluetoothConfig.PairingMode.fixedPin
|
||||
case .noPin:
|
||||
return Config.BluetoothConfig.PairingMode.noPin
|
||||
}
|
||||
}
|
||||
}
|
||||
48
Meshtastic/Enums/DeviceRoles.swift
Normal file
48
Meshtastic/Enums/DeviceRoles.swift
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// DeviceRoles.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright(c) Garth Vander Houwen 8/20/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// Default of 0 is Client
|
||||
enum DeviceRoles: Int, CaseIterable, Identifiable {
|
||||
|
||||
case client = 0
|
||||
case clientMute = 1
|
||||
case router = 2
|
||||
case routerClient = 3
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
|
||||
case .client:
|
||||
return "Client (default) - App connected client."
|
||||
case .clientMute:
|
||||
return "Client Mute - Same as a client except packets will not hop over this node, does not contribute to routing packets for mesh."
|
||||
case .router:
|
||||
return "Router - Mesh packets will prefer to be routed over this node. This node will not be used by client apps. The wifi/ble radios and the oled screen will be put to sleep."
|
||||
case .routerClient:
|
||||
return "Router Client - Mesh packets will prefer to be routed over this node. The Router Client can be used as both a Router and an app connected Client."
|
||||
}
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Config.DeviceConfig.Role {
|
||||
|
||||
switch self {
|
||||
|
||||
case .client:
|
||||
return Config.DeviceConfig.Role.client
|
||||
case .clientMute:
|
||||
return Config.DeviceConfig.Role.clientMute
|
||||
case .router:
|
||||
return Config.DeviceConfig.Role.router
|
||||
case .routerClient:
|
||||
return Config.DeviceConfig.Role.routerClient
|
||||
}
|
||||
}
|
||||
}
|
||||
56
Meshtastic/Enums/GpsFormats.swift
Normal file
56
Meshtastic/Enums/GpsFormats.swift
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
//
|
||||
// GpsFormats.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright(c) Garth Vander Houwen 8/20/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum GpsFormats: Int, CaseIterable, Identifiable {
|
||||
|
||||
case gpsFormatDec = 0
|
||||
case gpsFormatDms = 1
|
||||
case gpsFormatUtm = 2
|
||||
case gpsFormatMgrs = 3
|
||||
case gpsFormatOlc = 4
|
||||
case gpsFormatOsgr = 5
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .gpsFormatDec:
|
||||
return "Decimal Degrees Format"
|
||||
case .gpsFormatDms:
|
||||
return "Degrees Minutes Seconds"
|
||||
case .gpsFormatUtm:
|
||||
return "Universal Transverse Mercator"
|
||||
case .gpsFormatMgrs:
|
||||
return "Military Grid Reference System"
|
||||
case .gpsFormatOlc:
|
||||
return "Open Location Code (aka Plus Codes)"
|
||||
case .gpsFormatOsgr:
|
||||
return "Ordnance Survey Grid Reference"
|
||||
}
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Config.DisplayConfig.GpsCoordinateFormat {
|
||||
|
||||
switch self {
|
||||
|
||||
case .gpsFormatDec:
|
||||
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatDec
|
||||
case .gpsFormatDms:
|
||||
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatDms
|
||||
case .gpsFormatUtm:
|
||||
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatUtm
|
||||
case .gpsFormatMgrs:
|
||||
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatMgrs
|
||||
case .gpsFormatOlc:
|
||||
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatOlc
|
||||
case .gpsFormatOsgr:
|
||||
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatOsgr
|
||||
}
|
||||
}
|
||||
}
|
||||
181
Meshtastic/Enums/LoraConfigEnums.swift
Normal file
181
Meshtastic/Enums/LoraConfigEnums.swift
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
//
|
||||
// LoraConfig.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright(c) Garth Vander Houwen 8/20/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum RegionCodes : Int, CaseIterable, Identifiable {
|
||||
|
||||
case unset = 0
|
||||
case us = 1
|
||||
case eu433 = 2
|
||||
case eu868 = 3
|
||||
case cn = 4
|
||||
case jp = 5
|
||||
case anz = 6
|
||||
case kr = 7
|
||||
case tw = 8
|
||||
case ru = 9
|
||||
case `in` = 10
|
||||
case nz865 = 11
|
||||
case th = 12
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .unset:
|
||||
return "Please set a region"
|
||||
case .us:
|
||||
return "United States"
|
||||
case .eu433:
|
||||
return "European Union 433mhz"
|
||||
case .eu868:
|
||||
return "European Union 868mhz"
|
||||
case .cn:
|
||||
return "China"
|
||||
case .jp:
|
||||
return "Japan"
|
||||
case .anz:
|
||||
return "Australia / New Zealand"
|
||||
case .kr:
|
||||
return "Korea"
|
||||
case .tw:
|
||||
return "Taiwan"
|
||||
case .ru:
|
||||
return "Russia"
|
||||
case .in:
|
||||
return "India"
|
||||
case .nz865:
|
||||
return "New Zealand 865mhz"
|
||||
case .th:
|
||||
return "Thailand"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func protoEnumValue() -> Config.LoRaConfig.RegionCode {
|
||||
|
||||
switch self {
|
||||
|
||||
case .unset:
|
||||
return Config.LoRaConfig.RegionCode.unset
|
||||
case .us:
|
||||
return Config.LoRaConfig.RegionCode.us
|
||||
case .eu433:
|
||||
return Config.LoRaConfig.RegionCode.eu433
|
||||
case .eu868:
|
||||
return Config.LoRaConfig.RegionCode.eu868
|
||||
case .cn:
|
||||
return Config.LoRaConfig.RegionCode.cn
|
||||
case .jp:
|
||||
return Config.LoRaConfig.RegionCode.jp
|
||||
case .anz:
|
||||
return Config.LoRaConfig.RegionCode.anz
|
||||
case .kr:
|
||||
return Config.LoRaConfig.RegionCode.kr
|
||||
case .tw:
|
||||
return Config.LoRaConfig.RegionCode.tw
|
||||
case .ru:
|
||||
return Config.LoRaConfig.RegionCode.ru
|
||||
case .in:
|
||||
return Config.LoRaConfig.RegionCode.in
|
||||
case .nz865:
|
||||
return Config.LoRaConfig.RegionCode.nz865
|
||||
case .th:
|
||||
return Config.LoRaConfig.RegionCode.th
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ModemPresets : Int, CaseIterable, Identifiable {
|
||||
|
||||
case LongFast = 0
|
||||
case LongSlow = 1
|
||||
case VLongSlow = 2
|
||||
case MedSlow = 3
|
||||
case MedFast = 4
|
||||
case ShortSlow = 5
|
||||
case ShortFast = 6
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
|
||||
case .LongFast:
|
||||
return "Long Range - Fast"
|
||||
case .LongSlow:
|
||||
return "Long Range - Slow"
|
||||
case .VLongSlow:
|
||||
return "Very Long Range - Slow"
|
||||
case .MedSlow:
|
||||
return "Medium Range - Slow"
|
||||
case .MedFast:
|
||||
return "Medium Range - Fast"
|
||||
case .ShortSlow:
|
||||
return "Short Range - Slow"
|
||||
case .ShortFast:
|
||||
return "Short Range - Fast"
|
||||
}
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Config.LoRaConfig.ModemPreset {
|
||||
|
||||
switch self {
|
||||
|
||||
case .LongFast:
|
||||
return Config.LoRaConfig.ModemPreset.longFast
|
||||
case .LongSlow:
|
||||
return Config.LoRaConfig.ModemPreset.longSlow
|
||||
case .VLongSlow:
|
||||
return Config.LoRaConfig.ModemPreset.vlongSlow
|
||||
case .MedSlow:
|
||||
return Config.LoRaConfig.ModemPreset.medSlow
|
||||
case .MedFast:
|
||||
return Config.LoRaConfig.ModemPreset.medFast
|
||||
case .ShortSlow:
|
||||
return Config.LoRaConfig.ModemPreset.shortSlow
|
||||
case .ShortFast:
|
||||
return Config.LoRaConfig.ModemPreset.shortFast
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum HopValues : Int, CaseIterable, Identifiable {
|
||||
|
||||
case oneHop = 1
|
||||
case twoHops = 2
|
||||
case threeHops = 0
|
||||
case fourHops = 4
|
||||
case fiveHops = 5
|
||||
case sixHops = 6
|
||||
case sevenHops = 7
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
|
||||
case .oneHop:
|
||||
return "One Hop"
|
||||
case .twoHops:
|
||||
return "Two Hops"
|
||||
case .threeHops:
|
||||
return "Three Hops"
|
||||
case .fourHops:
|
||||
return "Four Hops"
|
||||
case .fiveHops:
|
||||
return "Five Hops"
|
||||
case .sixHops:
|
||||
return "Six Hops"
|
||||
case .sevenHops:
|
||||
return "Seven Hops"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
73
Meshtastic/Enums/ScreenIntervals.swift
Normal file
73
Meshtastic/Enums/ScreenIntervals.swift
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// ScreenIntervals.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright(c) Garth Vander Houwen 8/20/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// Default of 0 is One Minute
|
||||
enum ScreenOnIntervals: Int, CaseIterable, Identifiable {
|
||||
|
||||
case oneMinute = 60
|
||||
case fiveMinutes = 300
|
||||
case tenMinutes = 0
|
||||
case fifteenMinutes = 900
|
||||
case thirtyMinutes = 1800
|
||||
case oneHour = 3600
|
||||
case max = 31536000 // One Year
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .oneMinute:
|
||||
return "One Minute"
|
||||
case .fiveMinutes:
|
||||
return "Five Minutes"
|
||||
case .tenMinutes:
|
||||
return "Ten Minutes"
|
||||
case .fifteenMinutes:
|
||||
return "Fifteen Minutes"
|
||||
case .thirtyMinutes:
|
||||
return "Thirty Minutes"
|
||||
case .oneHour:
|
||||
return "One Hour"
|
||||
case .max:
|
||||
return "Always On"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default of 0 is off
|
||||
enum ScreenCarouselIntervals: Int, CaseIterable, Identifiable {
|
||||
|
||||
case off = 0
|
||||
case thirtySeconds = 30
|
||||
case oneMinute = 60
|
||||
case fiveMinutes = 300
|
||||
case tenMinutes = 600
|
||||
case fifteenMinutes = 900
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .off:
|
||||
return "Off"
|
||||
case .thirtySeconds:
|
||||
return "Thirty Seconds"
|
||||
case .oneMinute:
|
||||
return "One Minute"
|
||||
case .fiveMinutes:
|
||||
return "Five Minutes"
|
||||
case .tenMinutes:
|
||||
return "Ten Minutes"
|
||||
case .fifteenMinutes:
|
||||
return "Fifteen Minutes"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
42
Meshtastic/Enums/WiFiModes.swift
Normal file
42
Meshtastic/Enums/WiFiModes.swift
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// WiFiModes.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright(c) Garth Vander Houwen 8/20/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum WiFiModes: Int, CaseIterable, Identifiable {
|
||||
|
||||
case client = 0
|
||||
case accessPoint = 1
|
||||
case accessPointHidden = 2
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .client:
|
||||
return "Client"
|
||||
case .accessPoint:
|
||||
return "Software Access Point"
|
||||
case .accessPointHidden:
|
||||
return "Software Access Point (Hidden)"
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Config.WiFiConfig.WiFiMode {
|
||||
|
||||
switch self {
|
||||
|
||||
case .client:
|
||||
return Config.WiFiConfig.WiFiMode.client
|
||||
case .accessPoint:
|
||||
return Config.WiFiConfig.WiFiMode.accessPoint
|
||||
case .accessPointHidden:
|
||||
return Config.WiFiConfig.WiFiMode.accessPointHidden
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1048,6 +1048,35 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
return 0
|
||||
}
|
||||
|
||||
public func saveBluetoothConfig(config: Config.BluetoothConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 {
|
||||
|
||||
var adminPacket = AdminMessage()
|
||||
adminPacket.setConfig.bluetooth = config
|
||||
|
||||
var meshPacket: MeshPacket = MeshPacket()
|
||||
meshPacket.to = UInt32(connectedPeripheral.num)
|
||||
meshPacket.from = 0 //UInt32(connectedPeripheral.num)
|
||||
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
|
||||
meshPacket.priority = MeshPacket.Priority.reliable
|
||||
meshPacket.wantAck = true
|
||||
meshPacket.hopLimit = 0
|
||||
|
||||
var dataMessage = DataMessage()
|
||||
dataMessage.payload = try! adminPacket.serializedData()
|
||||
dataMessage.portnum = PortNum.adminApp
|
||||
|
||||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "Saved Bluetooth Config for \(toUser.longName ?? "Unknown")"
|
||||
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
public func saveDeviceConfig(config: Config.DeviceConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 {
|
||||
|
||||
var adminPacket = AdminMessage()
|
||||
|
|
|
|||
|
|
@ -23,6 +23,17 @@ extension Date {
|
|||
}
|
||||
}
|
||||
|
||||
extension Int {
|
||||
|
||||
func numberOfDigits() -> Int {
|
||||
if abs(self) < 10 {
|
||||
return 1
|
||||
} else {
|
||||
return 1 + (self/10).numberOfDigits()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
|
||||
/// Create `Data` from hexadecimal string representation
|
||||
|
|
|
|||
|
|
@ -92,6 +92,91 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont
|
|||
}
|
||||
}
|
||||
|
||||
if config.payloadVariant == Config.OneOf_PayloadVariant.bluetooth(config.bluetooth) {
|
||||
|
||||
var isDefault = false
|
||||
|
||||
if (try! config.bluetooth.jsonString()) == "{}" {
|
||||
|
||||
isDefault = true
|
||||
print("📶 Default Bluetooth config")
|
||||
if meshlogging { MeshLogger.log("🖥️ Default Bluetooth config \(String(nodeNum))") }
|
||||
|
||||
} else {
|
||||
|
||||
if meshlogging { MeshLogger.log("🖥️ Custom Bluetooth config \(String(nodeNum))") }
|
||||
print("📶 Custom Bluetooth config")
|
||||
}
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
// Found a node, save Device Config
|
||||
if !fetchedNode.isEmpty {
|
||||
|
||||
if fetchedNode[0].bluetoothConfig == nil {
|
||||
|
||||
let newBluetoothConfig = BluetoothConfigEntity(context: context)
|
||||
|
||||
if isDefault {
|
||||
|
||||
newBluetoothConfig.enabled = true
|
||||
newBluetoothConfig.mode = Int32(config.bluetooth.mode.rawValue)
|
||||
newBluetoothConfig.fixedPin = Int32("123456") ?? 123456
|
||||
|
||||
} else {
|
||||
|
||||
newBluetoothConfig.enabled = config.bluetooth.enabled
|
||||
newBluetoothConfig.mode = Int32(config.bluetooth.mode.rawValue)
|
||||
newBluetoothConfig.fixedPin = Int32(config.bluetooth.fixedPin)
|
||||
|
||||
}
|
||||
fetchedNode[0].bluetoothConfig = newBluetoothConfig
|
||||
|
||||
} else {
|
||||
|
||||
if isDefault {
|
||||
|
||||
fetchedNode[0].bluetoothConfig?.enabled = true
|
||||
fetchedNode[0].bluetoothConfig?.mode = Int32(config.bluetooth.mode.rawValue)
|
||||
fetchedNode[0].bluetoothConfig?.fixedPin = Int32("123456") ?? 123456
|
||||
|
||||
} else {
|
||||
|
||||
fetchedNode[0].bluetoothConfig?.enabled = config.bluetooth.enabled
|
||||
fetchedNode[0].bluetoothConfig?.mode = Int32(config.bluetooth.mode.rawValue)
|
||||
fetchedNode[0].bluetoothConfig?.fixedPin = Int32(config.bluetooth.fixedPin)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
try context.save()
|
||||
if meshlogging { MeshLogger.log("💾 Updated Bluetooth Config for node number: \(String(nodeNum))") }
|
||||
|
||||
} catch {
|
||||
|
||||
context.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Updating Core Data BluetoothConfigEntity: \(nsError)")
|
||||
}
|
||||
} else {
|
||||
|
||||
print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save Bluetooth Config")
|
||||
}
|
||||
|
||||
} catch {
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Fetching node for core data BluetoothConfigEntity failed: \(nsError)")
|
||||
}
|
||||
}
|
||||
|
||||
if config.payloadVariant == Config.OneOf_PayloadVariant.display(config.display) {
|
||||
|
||||
var isDefault = false
|
||||
|
|
@ -99,11 +184,12 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont
|
|||
if (try! config.display.jsonString()) == "{}" {
|
||||
|
||||
isDefault = true
|
||||
print("🖥️ Default Display config")
|
||||
|
||||
if meshlogging { MeshLogger.log("🖥️ Default Display config \(String(nodeNum))") }
|
||||
|
||||
} else {
|
||||
|
||||
print("🖥️ Custom Display config")
|
||||
if meshlogging { MeshLogger.log("🖥️ Custom Display config \(String(nodeNum))") }
|
||||
}
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>MeshtasticDataModel v 6.xcdatamodel</string>
|
||||
<string>MeshtasticDataModel v 7.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="20086" systemVersion="21G72" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="21G83" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<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"/>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,231 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="21G83" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="BluetoothConfigEntity" representedClassName="BluetoothConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="fixedPin" optional="YES" attributeType="Integer 32" defaultValueString="123456" usesScalarValueType="YES"/>
|
||||
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="num" attributeType="Integer 64" 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="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<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="DeviceConfigEntity" representedClassName="DeviceConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="debugLogEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="role" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="serialEnabled" optional="YES" attributeType="Boolean" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="deviceConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="deviceConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="DisplayConfigEntity" representedClassName="DisplayConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="compassNorthTop" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsFormat" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="num" attributeType="Integer 64" 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"/>
|
||||
<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="alertMessage" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="output" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="outputMilliseconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="externalNotificationConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="externalNotificationConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="LoRaConfigEntity" representedClassName="LoRaConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="hopLimit" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="modemPreset" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="regionCode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="txPower" optional="YES" attributeType="Integer 32" defaultValueString="0" 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="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="messageTimestamp" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="receivedACK" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="replyID" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="fromUser" maxCount="1" deletionRule="Nullify" ordered="YES" destinationEntity="UserEntity" inverseName="sentMessages" inverseEntity="UserEntity"/>
|
||||
<relationship name="toUser" 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="MyInfoEntity" representedClassName="MyInfoEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bitrate" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="bleName" optional="YES" attributeType="String"/>
|
||||
<attribute name="errorCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="firmwareVersion" attributeType="String"/>
|
||||
<attribute name="hasGps" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="hasWifi" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="maxChannels" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="messageTimeoutMsec" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="minAppVersion" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="myNodeNum" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="rebootCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="myInfoNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="myInfo" inverseEntity="NodeInfoEntity"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="myNodeNum"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="NodeInfoEntity" representedClassName="NodeInfoEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bleName" optional="YES" attributeType="String"/>
|
||||
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="lastHeard" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<relationship name="bluetoothConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="BluetoothConfigEntity" inverseName="bluetoothConfigNode" inverseEntity="BluetoothConfigEntity"/>
|
||||
<relationship name="cannedMessageConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="CannedMessageConfigEntity" inverseName="cannedMessagesConfigNode" inverseEntity="CannedMessageConfigEntity"/>
|
||||
<relationship name="deviceConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DeviceConfigEntity" inverseName="deviceConfigNode" inverseEntity="DeviceConfigEntity"/>
|
||||
<relationship name="displayConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DisplayConfigEntity" inverseName="displayConfigNode" inverseEntity="DisplayConfigEntity"/>
|
||||
<relationship name="externalNotificationConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="ExternalNotificationConfigEntity" inverseName="externalNotificationConfigNode" inverseEntity="ExternalNotificationConfigEntity"/>
|
||||
<relationship name="loRaConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="LoRaConfigEntity" inverseName="loRaConfigNode" inverseEntity="LoRaConfigEntity"/>
|
||||
<relationship name="myInfo" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="myInfoNode" inverseEntity="MyInfoEntity"/>
|
||||
<relationship name="positionConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PositionConfigEntity" inverseName="positionConfigNode" inverseEntity="PositionConfigEntity"/>
|
||||
<relationship name="positions" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PositionEntity" inverseName="nodePosition" inverseEntity="PositionEntity"/>
|
||||
<relationship name="rangeTestConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RangeTestConfigEntity" inverseName="rangeTestConfigNode" inverseEntity="RangeTestConfigEntity"/>
|
||||
<relationship name="serialConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SerialConfigEntity" inverseName="serialConfigNode" inverseEntity="SerialConfigEntity"/>
|
||||
<relationship name="telemetries" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TelemetryEntity" inverseName="nodeTelemetry" inverseEntity="TelemetryEntity"/>
|
||||
<relationship name="telemetryConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TelemetryConfigEntity" inverseName="telemetryConfigNode" inverseEntity="TelemetryConfigEntity"/>
|
||||
<relationship name="user" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="userNode" inverseEntity="UserEntity"/>
|
||||
<relationship name="wiFiConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="WiFiConfigEntity" inverseName="wiFiConfigNode" inverseEntity="WiFiConfigEntity"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="num"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="PositionConfigEntity" representedClassName="PositionConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="deviceGpsEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="fixedPosition" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsAttemptTime" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="positionBroadcastSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="smartPositionEnabled" optional="YES" attributeType="Boolean" 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="latitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="longitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="satsInView" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="nodePosition" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positions" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="RangeTestConfigEntity" representedClassName="RangeTestConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="num" attributeType="Integer 32" defaultValueString="0" 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="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="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="rxd" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="timeout" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="txd" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="serialConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="serialConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="TelemetryConfigEntity" representedClassName="TelemetryConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="deviceUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentDisplayFahrenheit" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentMeasurementEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentScreenEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="num" attributeType="Integer 64" 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="temperature" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="voltage" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<relationship name="nodeTelemetry" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetries" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="UserEntity" representedClassName="UserEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="hwModel" attributeType="String"/>
|
||||
<attribute name="isLicensed" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="longName" attributeType="String"/>
|
||||
<attribute name="macaddr" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="shortName" attributeType="String"/>
|
||||
<attribute name="team" optional="YES" 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="(toUser.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 isEmoji == false AND admin = false"/>
|
||||
</fetchedProperty>
|
||||
</entity>
|
||||
<entity name="WiFiConfigEntity" representedClassName="WiFiConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="password" optional="YES" attributeType="String" minValueString="0" maxValueString="60"/>
|
||||
<attribute name="ssid" optional="YES" attributeType="String" minValueString="0" maxValueString="30"/>
|
||||
<relationship name="wiFiConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="wiFiConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="CannedMessageConfigEntity" positionX="45" positionY="144" width="128" height="209"/>
|
||||
<element name="DeviceConfigEntity" positionX="45" positionY="144" width="128" height="104"/>
|
||||
<element name="DisplayConfigEntity" positionX="54" positionY="153" width="128" height="119"/>
|
||||
<element name="ExternalNotificationConfigEntity" positionX="63" positionY="162" width="128" height="149"/>
|
||||
<element name="LoRaConfigEntity" positionX="45" positionY="144" width="128" height="119"/>
|
||||
<element name="MessageEntity" positionX="-36" positionY="63" width="128" height="245"/>
|
||||
<element name="MyInfoEntity" positionX="-18" positionY="81" width="128" height="209"/>
|
||||
<element name="NodeInfoEntity" positionX="-63" positionY="-18" width="128" height="329"/>
|
||||
<element name="PositionConfigEntity" positionX="63" positionY="162" width="128" height="149"/>
|
||||
<element name="PositionEntity" positionX="-54" positionY="54" width="128" height="119"/>
|
||||
<element name="RangeTestConfigEntity" positionX="72" positionY="171" width="128" height="104"/>
|
||||
<element name="SerialConfigEntity" positionX="54" positionY="153" width="128" height="164"/>
|
||||
<element name="TelemetryConfigEntity" positionX="72" positionY="171" width="128" height="134"/>
|
||||
<element name="TelemetryEntity" positionX="160" positionY="192" width="128" height="209"/>
|
||||
<element name="UserEntity" positionX="0" positionY="144" width="128" height="230"/>
|
||||
<element name="WiFiConfigEntity" positionX="45" positionY="144" width="128" height="119"/>
|
||||
<element name="BluetoothConfigEntity" positionX="54" positionY="153" width="128" height="104"/>
|
||||
</elements>
|
||||
</model>
|
||||
22
Meshtastic/Views/Helpers/DistanceText.swift
Normal file
22
Meshtastic/Views/Helpers/DistanceText.swift
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
//
|
||||
// DistanceText.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright(c) Garth Vander Houwen 8/19/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import CoreLocation
|
||||
import MapKit
|
||||
|
||||
struct DistanceText: View {
|
||||
|
||||
var meters: CLLocationDistance
|
||||
|
||||
var body: some View {
|
||||
|
||||
let distanceFormatter = MKDistanceFormatter()
|
||||
|
||||
Text("Distance: \(distanceFormatter.string(fromDistance: Double(meters)))")
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
// A view showing a list of devices that have been seen on the mesh network from the perspective of the connected device.
|
||||
|
||||
import SwiftUI
|
||||
import CoreLocation
|
||||
|
||||
struct NodeList: View {
|
||||
|
||||
|
|
@ -35,8 +36,8 @@ struct NodeList: View {
|
|||
if nodes.count == 0 {
|
||||
|
||||
Text("Scan for Radios").font(.largeTitle)
|
||||
Text("No LoRa Mesh Nodes Found").font(.title2)
|
||||
Text("Go to the bluetooth section in the bottom right menu and click the Start Scanning button to scan for nearby radios and find your Meshtastic device. Make sure your device is powered on and near your phone or tablet.")
|
||||
Text("No Meshtastic Nodes Found").font(.title2)
|
||||
Text("Go to the bluetooth section in the bottom right menu and click the Start Scanning button to scan for nearby radios and find your Meshtastic device. Make sure your device is powered on and near your iPhone, iPad or Mac.")
|
||||
.font(.body)
|
||||
Text("Once the device shows under Available Devices touch the device you want to connect to and it will pull node information over BLE and populate the node list and mesh map in the Meshtastic app.")
|
||||
Text("Views with bluetooth functionality will show an indicator in the upper right hand corner show if bluetooth is on, and if a device is connected.")
|
||||
|
|
@ -87,14 +88,43 @@ struct NodeList: View {
|
|||
|
||||
Image(systemName: "clock.badge.checkmark.fill").font(.title3)
|
||||
.foregroundColor(.accentColor).symbolRenderingMode(.hierarchical)
|
||||
|
||||
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
|
||||
LastHeardText(lastHeard: node.lastHeard).font(.subheadline).foregroundColor(.gray)
|
||||
|
||||
} else {
|
||||
|
||||
LastHeardText(lastHeard: node.lastHeard).font(.title3).foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
|
||||
if node.positions?.count ?? 0 > 0 {
|
||||
|
||||
Spacer()
|
||||
HStack(alignment: .bottom) {
|
||||
|
||||
let lastPostion = node.positions!.reversed()[0] as! PositionEntity
|
||||
|
||||
let myCoord = CLLocation(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude)
|
||||
|
||||
let nodeCoord = CLLocation(latitude: lastPostion.coordinate!.latitude, longitude: lastPostion.coordinate!.longitude)
|
||||
|
||||
let metersAway = nodeCoord.distance(from: myCoord)
|
||||
|
||||
Image(systemName: "lines.measurement.horizontal").font(.title3)
|
||||
.foregroundColor(.accentColor).symbolRenderingMode(.hierarchical)
|
||||
|
||||
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
|
||||
DistanceText(meters: metersAway).font(.subheadline).foregroundColor(.gray)
|
||||
|
||||
} else {
|
||||
|
||||
DistanceText(meters: metersAway).font(.title3).foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding([.leading, .top, .bottom])
|
||||
}
|
||||
|
|
@ -109,13 +139,6 @@ struct NodeList: View {
|
|||
self.bleManager.userSettings = userSettings
|
||||
self.bleManager.context = context
|
||||
self.initialLoad = false
|
||||
|
||||
// if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
//
|
||||
// if nodes.count > 0 {
|
||||
// selection = "0"
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
167
Meshtastic/Views/Settings/Config/BluetoothConfig.swift
Normal file
167
Meshtastic/Views/Settings/Config/BluetoothConfig.swift
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
//
|
||||
// BluetoothConfig.swift
|
||||
// Meshtastic Apple
|
||||
//
|
||||
// Copyright (c) Garth Vander Houwen 8/18/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct BluetoothConfig: View {
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var initialLoad: Bool = true
|
||||
@State var hasChanges = false
|
||||
|
||||
@State var enabled = true
|
||||
/// Determines the pairing strategy for the device
|
||||
@State var mode = 0
|
||||
|
||||
/// Specified pin for PairingMode.FixedPin
|
||||
@State var fixedPin = "123456"
|
||||
|
||||
let numberFormatter: NumberFormatter = {
|
||||
let formatter = NumberFormatter()
|
||||
formatter.numberStyle = .none
|
||||
return formatter
|
||||
}()
|
||||
|
||||
var body: some View {
|
||||
|
||||
VStack {
|
||||
|
||||
Form {
|
||||
|
||||
Section(header: Text("Options")) {
|
||||
|
||||
Toggle(isOn: $enabled) {
|
||||
|
||||
Label("Enabled", systemImage: "antenna.radiowaves.left.and.right")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
|
||||
Picker("Pairing Mode", selection: $mode ) {
|
||||
ForEach(BluetoothModes.allCases) { bm in
|
||||
Text(bm.description)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
|
||||
if mode == 1 {
|
||||
|
||||
HStack {
|
||||
Label("Fixed PIN", systemImage: "wallet.pass")
|
||||
TextField("Fixed PIN", text: $fixedPin)
|
||||
.foregroundColor(.gray)
|
||||
.onChange(of: fixedPin, perform: { value in
|
||||
|
||||
let digitCount = fixedPin.utf8.count
|
||||
// Only mess with the value if it is too big
|
||||
if digitCount > 6 || digitCount < 6 {
|
||||
|
||||
fixedPin = "123456"
|
||||
}
|
||||
|
||||
if digitCount < 6 {
|
||||
|
||||
fixedPin = "123456"
|
||||
}
|
||||
})
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
.keyboardType(.decimalPad)
|
||||
}
|
||||
}
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
|
||||
Button {
|
||||
|
||||
isPresentingSaveConfirm = true
|
||||
|
||||
} label: {
|
||||
|
||||
Label("Save", systemImage: "square.and.arrow.down")
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil || !hasChanges)
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding()
|
||||
.confirmationDialog(
|
||||
|
||||
"Are you sure?",
|
||||
isPresented: $isPresentingSaveConfirm
|
||||
) {
|
||||
Button("Save Bluetooth Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") {
|
||||
|
||||
var bc = Config.BluetoothConfig()
|
||||
bc.enabled = enabled
|
||||
bc.mode = BluetoothModes(rawValue: mode)?.protoEnumValue() ?? Config.BluetoothConfig.PairingMode.randomPin
|
||||
bc.fixedPin = UInt32(fixedPin) ?? 123456
|
||||
|
||||
let adminMessageId = bleManager.saveBluetoothConfig(config: bc, fromUser: node!.user!, toUser: node!.user!)
|
||||
|
||||
if adminMessageId > 0 {
|
||||
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
hasChanges = false
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Display Config")
|
||||
.navigationBarItems(trailing:
|
||||
|
||||
ZStack {
|
||||
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
|
||||
})
|
||||
.onAppear {
|
||||
|
||||
if self.initialLoad{
|
||||
|
||||
self.bleManager.context = context
|
||||
|
||||
self.enabled = node!.bluetoothConfig?.enabled ?? true
|
||||
self.mode = Int(node!.bluetoothConfig?.mode ?? 0)
|
||||
//self.fixedPin = (String(node!.bluetoothConfig?.fixedPin) ?? "123456")
|
||||
self.hasChanges = false
|
||||
self.initialLoad = false
|
||||
}
|
||||
}
|
||||
.onChange(of: enabled) { newEnabled in
|
||||
|
||||
if node != nil && node!.bluetoothConfig != nil {
|
||||
|
||||
if newEnabled != node!.bluetoothConfig!.enabled { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: mode) { newMode in
|
||||
|
||||
if node != nil && node!.bluetoothConfig != nil {
|
||||
|
||||
if newMode != node!.bluetoothConfig!.mode { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: fixedPin) { newFixedPin in
|
||||
|
||||
if node != nil && node!.bluetoothConfig != nil {
|
||||
|
||||
if newFixedPin != String(node!.bluetoothConfig!.fixedPin) { hasChanges = true }
|
||||
}
|
||||
}
|
||||
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
}
|
||||
|
|
@ -6,46 +6,6 @@
|
|||
//
|
||||
import SwiftUI
|
||||
|
||||
// Default of 0 is Client
|
||||
enum DeviceRoles: Int, CaseIterable, Identifiable {
|
||||
|
||||
case client = 0
|
||||
case clientMute = 1
|
||||
case router = 2
|
||||
case routerClient = 3
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
|
||||
case .client:
|
||||
return "Client (default) - App connected client."
|
||||
case .clientMute:
|
||||
return "Client Mute - Same as a client except packets will not hop over this node, does not contribute to routing packets for mesh."
|
||||
case .router:
|
||||
return "Router - Mesh packets will prefer to be routed over this node. This node will not be used by client apps. The wifi/ble radios and the oled screen will be put to sleep."
|
||||
case .routerClient:
|
||||
return "Router Client - Mesh packets will prefer to be routed over this node. The Router Client can be used as both a Router and an app connected Client."
|
||||
}
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Config.DeviceConfig.Role {
|
||||
|
||||
switch self {
|
||||
|
||||
case .client:
|
||||
return Config.DeviceConfig.Role.client
|
||||
case .clientMute:
|
||||
return Config.DeviceConfig.Role.clientMute
|
||||
case .router:
|
||||
return Config.DeviceConfig.Role.router
|
||||
case .routerClient:
|
||||
return Config.DeviceConfig.Role.routerClient
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DeviceConfig: View {
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
|
|
|
|||
|
|
@ -7,119 +7,6 @@
|
|||
|
||||
import SwiftUI
|
||||
|
||||
enum GpsFormats: Int, CaseIterable, Identifiable {
|
||||
|
||||
case gpsFormatDec = 0
|
||||
case gpsFormatDms = 1
|
||||
case gpsFormatUtm = 2
|
||||
case gpsFormatMgrs = 3
|
||||
case gpsFormatOlc = 4
|
||||
case gpsFormatOsgr = 5
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .gpsFormatDec:
|
||||
return "Decimal Degrees Format"
|
||||
case .gpsFormatDms:
|
||||
return "Degrees Minutes Seconds"
|
||||
case .gpsFormatUtm:
|
||||
return "Universal Transverse Mercator"
|
||||
case .gpsFormatMgrs:
|
||||
return "Military Grid Reference System"
|
||||
case .gpsFormatOlc:
|
||||
return "Open Location Code (aka Plus Codes)"
|
||||
case .gpsFormatOsgr:
|
||||
return "Ordnance Survey Grid Reference"
|
||||
}
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Config.DisplayConfig.GpsCoordinateFormat {
|
||||
|
||||
switch self {
|
||||
|
||||
case .gpsFormatDec:
|
||||
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatDec
|
||||
case .gpsFormatDms:
|
||||
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatDms
|
||||
case .gpsFormatUtm:
|
||||
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatUtm
|
||||
case .gpsFormatMgrs:
|
||||
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatMgrs
|
||||
case .gpsFormatOlc:
|
||||
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatOlc
|
||||
case .gpsFormatOsgr:
|
||||
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatOsgr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default of 0 is One Minute
|
||||
enum ScreenOnIntervals: Int, CaseIterable, Identifiable {
|
||||
|
||||
case oneMinute = 60
|
||||
case fiveMinutes = 300
|
||||
case tenMinutes = 0
|
||||
case fifteenMinutes = 900
|
||||
case thirtyMinutes = 1800
|
||||
case oneHour = 3600
|
||||
case max = 31536000 // One Year
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .oneMinute:
|
||||
return "One Minute"
|
||||
case .fiveMinutes:
|
||||
return "Five Minutes"
|
||||
case .tenMinutes:
|
||||
return "Ten Minutes"
|
||||
case .fifteenMinutes:
|
||||
return "Fifteen Minutes"
|
||||
case .thirtyMinutes:
|
||||
return "Thirty Minutes"
|
||||
case .oneHour:
|
||||
return "One Hour"
|
||||
case .max:
|
||||
return "Always On"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default of 0 is off
|
||||
enum ScreenCarouselIntervals: Int, CaseIterable, Identifiable {
|
||||
|
||||
case off = 0
|
||||
case thirtySeconds = 30
|
||||
case oneMinute = 60
|
||||
case fiveMinutes = 300
|
||||
case tenMinutes = 600
|
||||
case fifteenMinutes = 900
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .off:
|
||||
return "Off"
|
||||
case .thirtySeconds:
|
||||
return "Thirty Seconds"
|
||||
case .oneMinute:
|
||||
return "One Minute"
|
||||
case .fiveMinutes:
|
||||
return "Five Minutes"
|
||||
case .tenMinutes:
|
||||
return "Ten Minutes"
|
||||
case .fifteenMinutes:
|
||||
return "Fifteen Minutes"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DisplayConfig: View {
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
|
|
|
|||
|
|
@ -7,179 +7,6 @@
|
|||
|
||||
import SwiftUI
|
||||
|
||||
enum RegionCodes : Int, CaseIterable, Identifiable {
|
||||
|
||||
case unset = 0
|
||||
case us = 1
|
||||
case eu433 = 2
|
||||
case eu868 = 3
|
||||
case cn = 4
|
||||
case jp = 5
|
||||
case anz = 6
|
||||
case kr = 7
|
||||
case tw = 8
|
||||
case ru = 9
|
||||
case `in` = 10
|
||||
case nz865 = 11
|
||||
case th = 12
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .unset:
|
||||
return "Please set a region"
|
||||
case .us:
|
||||
return "United States"
|
||||
case .eu433:
|
||||
return "European Union 433mhz"
|
||||
case .eu868:
|
||||
return "European Union 868mhz"
|
||||
case .cn:
|
||||
return "China"
|
||||
case .jp:
|
||||
return "Japan"
|
||||
case .anz:
|
||||
return "Australia / New Zealand"
|
||||
case .kr:
|
||||
return "Korea"
|
||||
case .tw:
|
||||
return "Taiwan"
|
||||
case .ru:
|
||||
return "Russia"
|
||||
case .in:
|
||||
return "India"
|
||||
case .nz865:
|
||||
return "New Zealand 865mhz"
|
||||
case .th:
|
||||
return "Thailand"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func protoEnumValue() -> Config.LoRaConfig.RegionCode {
|
||||
|
||||
switch self {
|
||||
|
||||
case .unset:
|
||||
return Config.LoRaConfig.RegionCode.unset
|
||||
case .us:
|
||||
return Config.LoRaConfig.RegionCode.us
|
||||
case .eu433:
|
||||
return Config.LoRaConfig.RegionCode.eu433
|
||||
case .eu868:
|
||||
return Config.LoRaConfig.RegionCode.eu868
|
||||
case .cn:
|
||||
return Config.LoRaConfig.RegionCode.cn
|
||||
case .jp:
|
||||
return Config.LoRaConfig.RegionCode.jp
|
||||
case .anz:
|
||||
return Config.LoRaConfig.RegionCode.anz
|
||||
case .kr:
|
||||
return Config.LoRaConfig.RegionCode.kr
|
||||
case .tw:
|
||||
return Config.LoRaConfig.RegionCode.tw
|
||||
case .ru:
|
||||
return Config.LoRaConfig.RegionCode.ru
|
||||
case .in:
|
||||
return Config.LoRaConfig.RegionCode.in
|
||||
case .nz865:
|
||||
return Config.LoRaConfig.RegionCode.nz865
|
||||
case .th:
|
||||
return Config.LoRaConfig.RegionCode.th
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ModemPresets : Int, CaseIterable, Identifiable {
|
||||
|
||||
case LongFast = 0
|
||||
case LongSlow = 1
|
||||
case VLongSlow = 2
|
||||
case MedSlow = 3
|
||||
case MedFast = 4
|
||||
case ShortSlow = 5
|
||||
case ShortFast = 6
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
|
||||
case .LongFast:
|
||||
return "Long Range - Fast"
|
||||
case .LongSlow:
|
||||
return "Long Range - Slow"
|
||||
case .VLongSlow:
|
||||
return "Very Long Range - Slow"
|
||||
case .MedSlow:
|
||||
return "Medium Range - Slow"
|
||||
case .MedFast:
|
||||
return "Medium Range - Fast"
|
||||
case .ShortSlow:
|
||||
return "Short Range - Slow"
|
||||
case .ShortFast:
|
||||
return "Short Range - Fast"
|
||||
}
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Config.LoRaConfig.ModemPreset {
|
||||
|
||||
switch self {
|
||||
|
||||
case .LongFast:
|
||||
return Config.LoRaConfig.ModemPreset.longFast
|
||||
case .LongSlow:
|
||||
return Config.LoRaConfig.ModemPreset.longSlow
|
||||
case .VLongSlow:
|
||||
return Config.LoRaConfig.ModemPreset.vlongSlow
|
||||
case .MedSlow:
|
||||
return Config.LoRaConfig.ModemPreset.medSlow
|
||||
case .MedFast:
|
||||
return Config.LoRaConfig.ModemPreset.medFast
|
||||
case .ShortSlow:
|
||||
return Config.LoRaConfig.ModemPreset.shortSlow
|
||||
case .ShortFast:
|
||||
return Config.LoRaConfig.ModemPreset.shortFast
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum HopValues : Int, CaseIterable, Identifiable {
|
||||
|
||||
case oneHop = 1
|
||||
case twoHops = 2
|
||||
case threeHops = 0
|
||||
case fourHops = 4
|
||||
case fiveHops = 5
|
||||
case sixHops = 6
|
||||
case sevenHops = 7
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
|
||||
case .oneHop:
|
||||
return "One Hop"
|
||||
case .twoHops:
|
||||
return "Two Hops"
|
||||
case .threeHops:
|
||||
return "Three Hops"
|
||||
case .fourHops:
|
||||
return "Four Hops"
|
||||
case .fiveHops:
|
||||
return "Five Hops"
|
||||
case .sixHops:
|
||||
return "Six Hops"
|
||||
case .sevenHops:
|
||||
return "Seven Hops"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LoRaConfig: View {
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
|
|
|
|||
|
|
@ -370,6 +370,13 @@ struct PositionConfig: View {
|
|||
if newSmartPosition != node!.positionConfig!.smartPositionEnabled { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: positionBroadcastSeconds) { newPositionBroadcastSeconds in
|
||||
|
||||
if node != nil && node!.positionConfig != nil {
|
||||
|
||||
if newPositionBroadcastSeconds != node!.positionConfig!.positionBroadcastSeconds { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: deviceGpsEnabled) { newDeviceGps in
|
||||
|
||||
if node != nil && node!.positionConfig != nil {
|
||||
|
|
|
|||
|
|
@ -7,40 +7,6 @@
|
|||
|
||||
import SwiftUI
|
||||
|
||||
enum WiFiModes: Int, CaseIterable, Identifiable {
|
||||
|
||||
case client = 0
|
||||
case accessPoint = 1
|
||||
case accessPointHidden = 2
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .client:
|
||||
return "Client"
|
||||
case .accessPoint:
|
||||
return "Software Access Point"
|
||||
case .accessPointHidden:
|
||||
return "Software Access Point (Hidden)"
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Config.WiFiConfig.WiFiMode {
|
||||
|
||||
switch self {
|
||||
|
||||
case .client:
|
||||
return Config.WiFiConfig.WiFiMode.client
|
||||
case .accessPoint:
|
||||
return Config.WiFiConfig.WiFiMode.accessPoint
|
||||
case .accessPointHidden:
|
||||
return Config.WiFiConfig.WiFiMode.accessPointHidden
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct WiFiConfig: View {
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
|
|
@ -125,12 +91,12 @@ struct WiFiConfig: View {
|
|||
// Only mess with the value if it is too big
|
||||
if totalBytes > 63 {
|
||||
|
||||
let firstNBytes = Data(ssid.utf8.prefix(63))
|
||||
let firstNBytes = Data(password.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
|
||||
ssid = maxBytesString
|
||||
password = maxBytesString
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -69,6 +69,18 @@ struct Settings: View {
|
|||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
|
||||
NavigationLink() {
|
||||
|
||||
BluetoothConfig(node: nodes.first(where: { $0.num == connectedNodeNum }))
|
||||
} label: {
|
||||
|
||||
Image(systemName: "antenna.radiowaves.left.and.right")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
|
||||
Text("Bluetooth (BLE)")
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
|
||||
NavigationLink {
|
||||
DeviceConfig(node: nodes.first(where: { $0.num == connectedNodeNum }))
|
||||
} label: {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue