Merge pull request #1308 from meshtastic/2.6.12

2.6.12 Working Changes
This commit is contained in:
Garth Vander Houwen 2025-07-15 09:47:25 -07:00 committed by GitHub
commit 6a908d8ff3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 2635 additions and 369 deletions

View file

@ -1,5 +1,10 @@
name: Upload dSYM Files
on:
push:
branches: [ main ]
workflow_dispatch:
jobs:
build:
runs-on: macos-latest
@ -8,14 +13,70 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- name: Generate/Download dSYM Files
uses: ./release.sh
- name: Select Xcode Version
run: sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
- name: Show Xcode Version
run: xcodebuild -version
- name: Setup Environment Variables
env:
DATADOG_CLIENT_TOKEN: ${{ secrets.DATADOG_CLIENT_TOKEN }}
run: |
echo "DATADOG_CLIENT_TOKEN=${DATADOG_CLIENT_TOKEN}" >> $GITHUB_ENV
- name: Build iOS App and Generate dSYMs
env:
DATADOG_CLIENT_TOKEN: ${{ secrets.DATADOG_CLIENT_TOKEN }}
run: |
# Create build directory
mkdir -p ./build/dSYMs
# Build iOS App Archive with dSYMs
xcodebuild \
-workspace Meshtastic.xcworkspace \
-scheme Meshtastic \
-configuration Release \
-destination 'generic/platform=iOS' \
-archivePath ./build/Meshtastic.xcarchive \
DATADOG_CLIENT_TOKEN="${DATADOG_CLIENT_TOKEN}" \
DEBUG_INFORMATION_FORMAT=dwarf-with-dsym \
DWARF_DSYM_FOLDER_PATH=./build/dSYMs \
archive
- name: Extract dSYMs from Archive
run: |
# Find and copy all dSYM files from the archive
find ./build/Meshtastic.xcarchive -name "*.dSYM" -exec cp -R {} ./build/dSYMs/ \;
# List what we found
echo "Found dSYM files:"
find ./build/dSYMs -name "*.dSYM" -type d
- name: Install Datadog CI
run: |
npm install -g @datadog/datadog-ci
- name: Upload dSYMs to Datadog
uses: DataDog/upload-dsyms-github-action@v1
env:
DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }}
DATADOG_SITE: datadoghq.com
run: |
# Upload all dSYM files to Datadog
if [ -d "./build/dSYMs" ] && [ "$(find ./build/dSYMs -name "*.dSYM" -type d | wc -l)" -gt 0 ]; then
echo "Uploading dSYM files to Datadog..."
datadog-ci dsyms upload ./build/dSYMs --dry-run=false
else
echo "No dSYM files found to upload"
exit 1
fi
- name: Upload Build Artifacts
uses: actions/upload-artifact@v4
if: always()
with:
api_key: ${{ secrets.DATADOG_API_KEY }}
site: datadoghq.com
dsym_paths: |
path/to/dsyms/folder
path/to/zip/dsyms.zip
name: dsym-files
path: |
./build/dSYMs
./build/Meshtastic.xcarchive
retention-days: 30

View file

@ -28,8 +28,8 @@ file_length:
# TODO: should review
cyclomatic_complexity:
warning: 60
error: 70
warning: 70
error: 80
ignores_case_statements: true
# TODO: should review
@ -57,4 +57,4 @@ custom_rules:
name: "Disable `print()`"
regex: "((\\bprint)|(Swift\\.print))\\s*\\("
message: "Consider using a dedicated log message or the Xcode debugger instead of using `print`. ex. logger.debug(...)"
severity: warning
severity: warning

File diff suppressed because it is too large Load diff

View file

@ -13,6 +13,8 @@
102B5EB12E172F41003D191E /* DatadogRUM in Frameworks */ = {isa = PBXBuildFile; productRef = 102B5EB02E172F41003D191E /* DatadogRUM */; };
108FFECB2DD3F43C00BFAA81 /* ShareContactQRDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 108FFECA2DD3F43C00BFAA81 /* ShareContactQRDialog.swift */; };
108FFECD2DD4005600BFAA81 /* NodeInfoEntityToNodeInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 108FFECC2DD4005600BFAA81 /* NodeInfoEntityToNodeInfo.swift */; };
10D109F22E2047D600536CE6 /* DatadogSessionReplay in Frameworks */ = {isa = PBXBuildFile; productRef = 10D109F12E2047D600536CE6 /* DatadogSessionReplay */; };
10D109F42E2047D600536CE6 /* DatadogTrace in Frameworks */ = {isa = PBXBuildFile; productRef = 10D109F32E2047D600536CE6 /* DatadogTrace */; };
231B3F212D087A4C0069A07D /* MetricTableColumn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 231B3F202D087A4C0069A07D /* MetricTableColumn.swift */; };
231B3F222D087A4C0069A07D /* MetricsColumnList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 231B3F1F2D087A4C0069A07D /* MetricsColumnList.swift */; };
231B3F252D087C3C0069A07D /* EnvironmentDefaultColumns.swift in Sources */ = {isa = PBXBuildFile; fileRef = 231B3F242D087C3C0069A07D /* EnvironmentDefaultColumns.swift */; };
@ -145,6 +147,7 @@
DD6F65792C6EADE60053C113 /* DirectMessagesHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6F65782C6EADE60053C113 /* DirectMessagesHelp.swift */; };
DD6F657B2C6EC2900053C113 /* LockLegend.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6F657A2C6EC2900053C113 /* LockLegend.swift */; };
DD73FD1128750779000852D6 /* PositionLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD73FD1028750779000852D6 /* PositionLog.swift */; };
DD74ED0D2DC6A0C90059AC10 /* DeviceOnboarding.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD74ED0C2DC6A0B80059AC10 /* DeviceOnboarding.swift */; };
DD769E0328D18BF1001A3F05 /* DeviceMetricsLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD769E0228D18BF0001A3F05 /* DeviceMetricsLog.swift */; };
DD77093B2AA1ABB8007A8BF0 /* BluetoothTips.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD77093A2AA1ABB8007A8BF0 /* BluetoothTips.swift */; };
DD77093D2AA1AFA3007A8BF0 /* ChannelTips.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD77093C2AA1AFA3007A8BF0 /* ChannelTips.swift */; };
@ -200,7 +203,6 @@
DDC2E15C26CE248F0042C5E4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */; };
DDC2E15F26CE248F0042C5E4 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15E26CE248F0042C5E4 /* Preview Assets.xcassets */; };
DDC2E18F26CE25FE0042C5E4 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E18E26CE25FE0042C5E4 /* ContentView.swift */; };
DDC2E1A726CEB3400042C5E4 /* LocationHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E1A626CEB3400042C5E4 /* LocationHelper.swift */; };
DDC4C9FF2A8D982900CE201C /* DetectionSensorConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC4C9FE2A8D982900CE201C /* DetectionSensorConfig.swift */; };
DDC4D568275499A500A4208E /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC4D567275499A500A4208E /* Persistence.swift */; };
DDC94FC129CE063B0082EA6E /* BatteryLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC94FC029CE063B0082EA6E /* BatteryLevel.swift */; };
@ -445,6 +447,7 @@
DD6F65782C6EADE60053C113 /* DirectMessagesHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DirectMessagesHelp.swift; sourceTree = "<group>"; };
DD6F657A2C6EC2900053C113 /* LockLegend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockLegend.swift; sourceTree = "<group>"; };
DD73FD1028750779000852D6 /* PositionLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionLog.swift; sourceTree = "<group>"; };
DD74ED0C2DC6A0B80059AC10 /* DeviceOnboarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceOnboarding.swift; sourceTree = "<group>"; };
DD769E0228D18BF0001A3F05 /* DeviceMetricsLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceMetricsLog.swift; sourceTree = "<group>"; };
DD77093A2AA1ABB8007A8BF0 /* BluetoothTips.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothTips.swift; sourceTree = "<group>"; };
DD77093C2AA1AFA3007A8BF0 /* ChannelTips.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelTips.swift; sourceTree = "<group>"; };
@ -514,7 +517,6 @@
DDC2E15E26CE248F0042C5E4 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
DDC2E16526CE248F0042C5E4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
DDC2E18E26CE25FE0042C5E4 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
DDC2E1A626CEB3400042C5E4 /* LocationHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationHelper.swift; sourceTree = "<group>"; };
DDC4C9FE2A8D982900CE201C /* DetectionSensorConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetectionSensorConfig.swift; sourceTree = "<group>"; };
DDC4CA012A8DAA3800CE201C /* MeshtasticDataModelV16.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV16.xcdatamodel; sourceTree = "<group>"; };
DDC4D567275499A500A4208E /* Persistence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
@ -599,9 +601,11 @@
102B5EAD2E172F41003D191E /* DatadogCrashReporting in Frameworks */,
25A978BA2C13F8ED0003AAE7 /* MeshtasticProtobufs in Frameworks */,
102B5EAB2E172F41003D191E /* DatadogCore in Frameworks */,
10D109F22E2047D600536CE6 /* DatadogSessionReplay in Frameworks */,
102B5EAF2E172F41003D191E /* DatadogLogs in Frameworks */,
102B5EB12E172F41003D191E /* DatadogRUM in Frameworks */,
DD0D3D222A55CEB10066DB71 /* CocoaMQTT in Frameworks */,
10D109F42E2047D600536CE6 /* DatadogTrace in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -871,6 +875,14 @@
path = Help;
sourceTree = "<group>";
};
DD74ED0B2DC6A0900059AC10 /* Onboarding */ = {
isa = PBXGroup;
children = (
DD74ED0C2DC6A0B80059AC10 /* DeviceOnboarding.swift */,
);
path = Onboarding;
sourceTree = "<group>";
};
DD7709392AA1ABA1007A8BF0 /* Tips */ = {
isa = PBXGroup;
children = (
@ -1010,11 +1022,12 @@
DDC2E18726CE24E40042C5E4 /* Views */ = {
isa = PBXGroup;
children = (
DD6D5A312CA1176A00ED3032 /* Layouts */,
DDC2E18D26CE25CB0042C5E4 /* Helpers */,
DD47E3D726F2F21A00029299 /* Bluetooth */,
DDC2E18D26CE25CB0042C5E4 /* Helpers */,
DD6D5A312CA1176A00ED3032 /* Layouts */,
DDC2E18B26CE25A70042C5E4 /* Messages */,
DD47E3CA26F0E50300029299 /* Nodes */,
DD74ED0B2DC6A0900059AC10 /* Onboarding */,
DD4A911C2708C57100501B7E /* Settings */,
DDC2E18E26CE25FE0042C5E4 /* ContentView.swift */,
);
@ -1090,7 +1103,6 @@
DDD43FE12A78C86B0083A3E9 /* Mqtt */,
DDAF8C5226EB1DF10058C060 /* BLEManager.swift */,
DD1BEF492E0292220090CE24 /* KeychainHelper.swift */,
DDC2E1A626CEB3400042C5E4 /* LocationHelper.swift */,
DD913638270DFF4C00D7ACF3 /* LocalNotificationManager.swift */,
DDA6B2E828419CF2003E8C16 /* MeshPackets.swift */,
DD964FBC296E6B01007C176F /* EmojiOnlyTextField.swift */,
@ -1248,6 +1260,8 @@
102B5EAC2E172F41003D191E /* DatadogCrashReporting */,
102B5EAE2E172F41003D191E /* DatadogLogs */,
102B5EB02E172F41003D191E /* DatadogRUM */,
10D109F12E2047D600536CE6 /* DatadogSessionReplay */,
10D109F32E2047D600536CE6 /* DatadogTrace */,
);
productName = MeshtasticClient;
productReference = DDC2E15426CE248E0042C5E4 /* Meshtastic.app */;
@ -1419,7 +1433,6 @@
6DEDA55A2A957B8E00321D2E /* DetectionSensorLog.swift in Sources */,
DD798B072915928D005217CD /* ChannelMessageList.swift in Sources */,
231B3F272D0885240069A07D /* MetricsColumnDetail.swift in Sources */,
DDC2E1A726CEB3400042C5E4 /* LocationHelper.swift in Sources */,
DD77093D2AA1AFA3007A8BF0 /* ChannelTips.swift in Sources */,
6D825E622C34786C008DBEE4 /* CommonRegex.swift in Sources */,
DD913639270DFF4C00D7ACF3 /* LocalNotificationManager.swift in Sources */,
@ -1468,6 +1481,7 @@
DDDB263F2AABEE20003AFCB7 /* NodeList.swift in Sources */,
DDD5BB0B2C285E45007E03CA /* LogDetail.swift in Sources */,
DDA0B6B2294CDC55001356EC /* Channels.swift in Sources */,
DD74ED0D2DC6A0C90059AC10 /* DeviceOnboarding.swift in Sources */,
DDE9659C2B1C3B6A00531070 /* RouteRecorder.swift in Sources */,
B399E8A42B6F486400E4488E /* RetryButton.swift in Sources */,
DDB8F4102A9EE5B400230ECE /* Messages.swift in Sources */,
@ -1842,7 +1856,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.6.11;
MARKETING_VERSION = 2.6.12;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTS_MACCATALYST = YES;
@ -1875,7 +1889,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.6.11;
MARKETING_VERSION = 2.6.12;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTS_MACCATALYST = YES;
@ -1906,7 +1920,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.6.11;
MARKETING_VERSION = 2.6.12;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -1938,7 +1952,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.6.11;
MARKETING_VERSION = 2.6.12;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -2046,6 +2060,16 @@
package = 102B5EA92E172F41003D191E /* XCRemoteSwiftPackageReference "dd-sdk-ios" */;
productName = DatadogRUM;
};
10D109F12E2047D600536CE6 /* DatadogSessionReplay */ = {
isa = XCSwiftPackageProductDependency;
package = 102B5EA92E172F41003D191E /* XCRemoteSwiftPackageReference "dd-sdk-ios" */;
productName = DatadogSessionReplay;
};
10D109F32E2047D600536CE6 /* DatadogTrace */ = {
isa = XCSwiftPackageProductDependency;
package = 102B5EA92E172F41003D191E /* XCRemoteSwiftPackageReference "dd-sdk-ios" */;
productName = DatadogTrace;
};
25A978B92C13F8ED0003AAE7 /* MeshtasticProtobufs */ = {
isa = XCSwiftPackageProductDependency;
productName = MeshtasticProtobufs;

View file

@ -1,5 +1,5 @@
{
"originHash" : "0dabe052e9e56f8514254d01df9aa7245e16b28a649d59bac6781d4ac9a79efa",
"originHash" : "fd71b247ba909b0eb360db5530e1068363839c5e169dea6f6a9974b2d98276f4",
"pins" : [
{
"identity" : "cocoamqtt",

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "wio_tracker_l1_case.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,710 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="431.09 27.94 434.45 897.75474"
version="1.1"
id="svg97"
sodipodi:docname="wio_tracker_l1_case.svg"
inkscape:version="1.4 (e7c3feb1, 2024-10-09)"
width="434.45001"
height="897.75476"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview97"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="0.81923274"
inkscape:cx="149.53016"
inkscape:cy="340.56256"
inkscape:window-width="1104"
inkscape:window-height="875"
inkscape:window-x="7"
inkscape:window-y="46"
inkscape:window-maximized="0"
inkscape:current-layer="svg97" />
<defs
id="defs1">
<style
id="style1">.cls-1{fill:#3b3b3d;}.cls-2{fill:#242426;}.cls-3{fill:#ec6263;}.cls-4{fill:#595859;}.cls-5{fill:#4c4b4f;}.cls-6{fill:#eb612a;}.cls-7{fill:#f6a75e;}.cls-8{fill:#e0b77b;}.cls-9{fill:#1a1a1c;}.cls-10,.cls-11,.cls-12,.cls-13{fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;}.cls-10{stroke-width:1.36px;}.cls-11{stroke-width:1.34px;}.cls-12{stroke-width:1.23px;}.cls-13{stroke-width:1.46px;}.cls-14{fill:#333335;}</style>
</defs>
<g
id="Layer_2"
data-name="Layer 2"
transform="translate(124,-118.76523)">
<path
class="cls-1"
d="m 740.86,685.6 v 336.65 a 21.54,21.54 0 0 1 -21.54,21.53 H 458.69 a 21.55,21.55 0 0 1 -21.55,-21.53 V 655.32 A 17.23,17.23 0 0 1 454.36,638.1 H 555 a 11.72,11.72 0 0 1 8.14,3.29 l 13.48,13 8.94,8.65 a 11.67,11.67 0 0 0 8.13,3.29 h 127.92 a 19.25,19.25 0 0 1 19.25,19.27 z"
id="path1" />
<path
class="cls-2"
d="m 619.43,660.89 -11.08,-11.08 a 5.51,5.51 0 0 0 -4,-1.69 h -21.9 a 5.71,5.71 0 0 0 -5.77,5.71 v 0.59 l -13.48,-13 h 45.71 a 16.37,16.37 0 0 1 11.57,4.8 l 20.16,20.17 h -23.13 a 3.2,3.2 0 0 0 1.92,-5.5 z"
id="path2" />
<path
class="cls-1"
d="m 617.51,666.36 a 2.11,2.11 0 0 1 -0.36,0 h -19 v 0 z"
id="path3" />
<path
class="cls-2"
d="m 437.14,874.52 -3.33,-3.33 a 7,7 0 0 1 -2,-5 V 764.1 a 7,7 0 0 1 2,-4.94 l 3.33,-3.33 z"
id="path4" />
<path
class="cls-3"
d="m 431.77,774.2 v 0 a 2.7,2.7 0 0 1 2.69,2.69 v 19.68 a 2.69,2.69 0 0 1 -2.69,2.69 v 0 z"
id="path5" />
<path
class="cls-3"
d="m 431.77,836.51 v 0 a 2.69,2.69 0 0 1 2.69,2.69 v 19.68 a 2.7,2.7 0 0 1 -2.69,2.69 v 0 z"
id="path6" />
<rect
class="cls-1"
x="648.89001"
y="604.95721"
width="52.139999"
height="45.419998"
rx="1.47"
id="rect9" />
<rect
class="cls-1"
x="645.45001"
y="611.43719"
width="58.369999"
height="33.209999"
rx="1.9299999"
id="rect10" />
<path
class="cls-2"
d="m 676.07,743.46 v 84.2 a 5.37,5.37 0 0 1 -5.35,5.35 H 509.27 a 5.36,5.36 0 0 1 -5.35,-5.35 v -84.2 a 5.34,5.34 0 0 1 5.35,-5.35 h 161.45 a 5.36,5.36 0 0 1 5.35,5.35 z"
id="path10" />
<path
class="cls-4"
d="M 672.07,844.7 H 507.91 a 16,16 0 0 1 -16,-16 v -86.28 a 16,16 0 0 1 16,-16 h 91.35 a 10.82,10.82 0 0 0 5.85,-1.71 L 623,713.22 a 17.64,17.64 0 0 1 9.57,-2.8 h 40.58 a 14.9,14.9 0 0 1 14.88,14.88 V 828.68 A 16,16 0 0 1 672.07,844.7 Z M 507.89,733.33 a 9.1,9.1 0 0 0 -9.09,9.09 v 86.26 a 9.12,9.12 0 0 0 9.11,9.11 h 164.16 a 9.11,9.11 0 0 0 9.09,-9.11 V 725.3 a 8,8 0 0 0 -8,-8 h -40.55 a 10.82,10.82 0 0 0 -5.85,1.71 l -18,11.49 a 17.73,17.73 0 0 1 -9.57,2.8 z"
id="path11" />
<path
class="cls-5"
d="m 673.19,713.87 h -40.57 a 14.36,14.36 0 0 0 -7.71,2.25 L 607,727.62 a 14.33,14.33 0 0 1 -7.72,2.25 H 507.9 a 12.56,12.56 0 0 0 -12.55,12.55 v 86.26 a 12.56,12.56 0 0 0 12.56,12.56 h 164.16 a 12.55,12.55 0 0 0 12.55,-12.56 V 725.3 a 11.42,11.42 0 0 0 -11.43,-11.43 z m 2.88,113.79 a 5.37,5.37 0 0 1 -5.35,5.35 H 509.27 a 5.36,5.36 0 0 1 -5.35,-5.35 v -84.2 a 5.34,5.34 0 0 1 5.35,-5.35 h 161.45 a 5.36,5.36 0 0 1 5.35,5.35 z"
id="path12" />
<circle
class="cls-5"
cx="628.69"
cy="926.5"
r="18.08"
id="circle12" />
<circle
class="cls-5"
cx="691.77002"
cy="926.5"
r="25.82"
id="circle13" />
<circle
class="cls-6"
cx="628.69"
cy="926.5"
r="14.31"
id="circle14" />
<circle
class="cls-6"
cx="691.77002"
cy="926.5"
r="21.16"
id="circle15" />
<circle
class="cls-7"
cx="628.69"
cy="926.5"
r="11.36"
id="circle16" />
<circle
class="cls-7"
cx="691.77002"
cy="926.5"
r="17.809999"
id="circle17" />
<rect
class="cls-8"
x="647.82001"
y="653.4472"
width="55.25"
height="7.02"
id="rect17" />
<rect
class="cls-8"
x="650.88"
y="650.57721"
width="48.700001"
height="1.9299999"
id="rect19" />
<rect
class="cls-2"
x="645.45001"
y="614.28723"
width="58.369999"
height="27.76"
id="rect20" />
<rect
class="cls-2"
x="653.03003"
y="601.79724"
width="44.41"
height="2.9400001"
id="rect21" />
<rect
class="cls-1"
x="650.88"
y="537.74719"
width="48.700001"
height="64.050003"
rx="2.3800001"
id="rect22" />
<path
class="cls-2"
d="m 709.54,662.52 v 3.84 h -68 v -3.84 a 1.85,1.85 0 0 1 1.85,-1.85 h 64.34 a 1.85,1.85 0 0 1 1.81,1.85 z"
id="path22" />
<rect
class="cls-5"
x="506.41"
y="970.47998"
width="170.03"
height="9.9700003"
rx="4.98"
id="rect24" />
<rect
class="cls-5"
x="506.41"
y="1004.91"
width="170.03"
height="9.9700003"
rx="4.98"
id="rect25" />
<path
class="cls-5"
d="m 573.49,709.25 h -4.44 a 3.53,3.53 0 0 1 -2.47,-1 l -42.93,-42.7 a 1.17,1.17 0 0 1 0.83,-2 h 4.43 a 3.5,3.5 0 0 1 2.48,1 l 42.93,42.7 a 1.18,1.18 0 0 1 -0.83,2 z"
id="path25" />
<path
class="cls-5"
d="m 546.14,709.25 h -4.44 a 3.5,3.5 0 0 1 -2.47,-1 l -42.94,-42.7 a 1.18,1.18 0 0 1 0.83,-2 h 4.44 a 3.49,3.49 0 0 1 2.47,1 L 547,707.24 a 1.17,1.17 0 0 1 -0.86,2.01 z"
id="path26" />
<path
class="cls-5"
d="m 518.78,709.25 h -4.44 a 3.53,3.53 0 0 1 -2.47,-1 l -42.93,-42.7 a 1.17,1.17 0 0 1 0.82,-2 h 4.44 a 3.45,3.45 0 0 1 2.47,1 l 42.94,42.7 a 1.18,1.18 0 0 1 -0.83,2 z"
id="path27" />
<path
class="cls-5"
d="m 486.12,709.26 h -4.44 a 3.5,3.5 0 0 1 -2.47,-1 l -7,-7 a 1.17,1.17 0 0 1 0.83,-2 h 4.44 a 3.49,3.49 0 0 1 2.47,1 l 7,7 a 1.18,1.18 0 0 1 -0.83,2 z"
id="path28" />
<path
class="cls-5"
d="M 721.61,666.36 H 593.73 a 11.67,11.67 0 0 1 -8.13,-3.29 l -8.94,-8.65 -13.48,-13 A 11.72,11.72 0 0 0 555,638.1 H 454.36 a 17.23,17.23 0 0 0 -17.22,17.22 v 366.93 a 21.55,21.55 0 0 0 21.55,21.53 h 260.63 a 21.54,21.54 0 0 0 21.54,-21.53 V 685.6 a 19.25,19.25 0 0 0 -19.25,-19.24 z m 13,355.89 a 15.33,15.33 0 0 1 -15.33,15.31 H 458.69 a 15.35,15.35 0 0 1 -15.33,-15.31 V 655.32 a 11,11 0 0 1 11,-11 H 555 a 5.42,5.42 0 0 1 3.81,1.55 l 22.43,21.67 a 17.86,17.86 0 0 0 12.45,5 h 127.92 a 13.05,13.05 0 0 1 13,13 z"
id="path29" />
<path
class="cls-10"
d="m 437.15,655.32 v 366.92 a 21.54,21.54 0 0 0 21.54,21.54 h 260.63 a 21.54,21.54 0 0 0 21.54,-21.54 V 685.6 A 19.24,19.24 0 0 0 721.62,666.37 H 593.73 a 11.7,11.7 0 0 1 -8.13,-3.29 L 563.17,641.39 A 11.7,11.7 0 0 0 555,638.1 H 454.36 a 17.21,17.21 0 0 0 -17.21,17.22 z"
id="path30" />
<path
class="cls-10"
d="m 563.17,641.39 h 45.71 a 16.42,16.42 0 0 1 11.58,4.79 l 20.15,20.19"
id="path31" />
<path
class="cls-10"
d="m 576.67,654.42 v -0.6 a 5.72,5.72 0 0 1 5.76,-5.71 h 21.9 a 5.53,5.53 0 0 1 4,1.69 l 11.09,11.08 a 3.21,3.21 0 0 1 -2.29,5.49 h -19"
id="path32" />
<path
class="cls-10"
d="m 641.49,666.37 v -3.66 a 2.05,2.05 0 0 1 2,-2.05 h 64 a 2.05,2.05 0 0 1 2,2.05 v 3.66"
id="path33" />
<path
class="cls-10"
d="m 697.27,660.95719 h 4.94 a 0.87,0.87 0 0 0 0.86,-0.86 v -6.14 a 0.86,0.86 0 0 0 -0.86,-0.86 h -53.64 a 1,1 0 0 0 -1,1 v 5.84 a 1,1 0 0 0 1,1 h 5.17"
id="path34" />
<path
class="cls-11"
d="m 701,644.65719 v 4.5 a 1.22,1.22 0 0 1 -1.21,1.22 H 650 a 1.2,1.2 0 0 1 -1.21,-1.22 v -4.5"
id="path35" />
<path
class="cls-11"
d="m 648.8,611.43719 v -5.27 a 1.21,1.21 0 0 1 1.21,-1.21 h 49.81 a 1.22,1.22 0 0 1 1.21,1.21 v 5.27"
id="path36" />
<line
class="cls-11"
x1="650.88"
y1="650.3772"
x2="650.88"
y2="653.09723"
id="line36" />
<line
class="cls-11"
x1="699.58002"
y1="650.3772"
x2="699.58002"
y2="653.09723"
id="line37" />
<rect
class="cls-11"
x="650.88"
y="537.74719"
width="48.700001"
height="64.050003"
rx="2.1400001"
id="rect37" />
<line
class="cls-11"
x1="653.03003"
y1="601.79724"
x2="653.03003"
y2="604.95721"
id="line38" />
<line
class="cls-11"
x1="697.44"
y1="601.79724"
x2="697.44"
y2="604.95721"
id="line39" />
<rect
class="cls-11"
x="645.45001"
y="611.43719"
width="58.369999"
height="33.209999"
rx="1.92"
id="rect45" />
<line
class="cls-11"
x1="648.89001"
y1="614.28723"
x2="648.89001"
y2="642.04724"
id="line45" />
<line
class="cls-11"
x1="653.63"
y1="614.28723"
x2="653.63"
y2="642.04724"
id="line46" />
<line
class="cls-11"
x1="658.35999"
y1="614.28723"
x2="658.35999"
y2="642.04724"
id="line47" />
<line
class="cls-11"
x1="663.09003"
y1="614.28723"
x2="663.09003"
y2="642.04724"
id="line48" />
<line
class="cls-11"
x1="667.82001"
y1="614.28723"
x2="667.82001"
y2="642.04724"
id="line49" />
<line
class="cls-11"
x1="672.54999"
y1="614.28723"
x2="672.54999"
y2="642.04724"
id="line50" />
<line
class="cls-11"
x1="677.28003"
y1="614.28723"
x2="677.28003"
y2="642.04724"
id="line51" />
<line
class="cls-11"
x1="682.01001"
y1="614.28723"
x2="682.01001"
y2="642.04724"
id="line52" />
<line
class="cls-11"
x1="686.73999"
y1="614.28723"
x2="686.73999"
y2="642.04724"
id="line53" />
<line
class="cls-11"
x1="691.46997"
y1="614.28723"
x2="691.46997"
y2="642.04724"
id="line54" />
<line
class="cls-11"
x1="696.20001"
y1="614.28723"
x2="696.20001"
y2="642.04724"
id="line55" />
<line
class="cls-11"
x1="700.92999"
y1="614.28723"
x2="700.92999"
y2="642.04724"
id="line56" />
<path
class="cls-13"
d="m 676.07,743.46 v 84.2 a 5.37,5.37 0 0 1 -5.35,5.35 H 509.27 a 5.36,5.36 0 0 1 -5.35,-5.35 v -84.2 a 5.34,5.34 0 0 1 5.35,-5.35 h 161.45 a 5.36,5.36 0 0 1 5.35,5.35 z"
id="path56" />
<path
class="cls-13"
d="m 673.19,713.87 a 11.43,11.43 0 0 1 11.43,11.43 v 103.38 a 12.55,12.55 0 0 1 -12.55,12.56 H 507.91 a 12.56,12.56 0 0 1 -12.56,-12.56 v -86.26 a 12.55,12.55 0 0 1 12.55,-12.55 h 91.34 a 14.33,14.33 0 0 0 7.72,-2.25 l 17.94,-11.49 a 14.37,14.37 0 0 1 7.72,-2.26 z"
id="path57" />
<circle
class="cls-5"
cx="468.76001"
cy="746.5"
r="20.200001"
id="circle57" />
<circle
class="cls-2"
cx="468.76001"
cy="746.5"
r="11.67"
id="circle58" />
<circle
class="cls-14"
cx="468.76001"
cy="746.5"
r="9.3299999"
id="circle59" />
<circle
class="cls-13"
cx="468.76001"
cy="746.5"
r="20.200001"
id="circle60" />
<circle
class="cls-13"
cx="468.76001"
cy="746.5"
r="9.3299999"
id="circle61" />
<circle
class="cls-13"
cx="468.76001"
cy="746.5"
r="11.67"
id="circle62" />
<line
class="cls-13"
x1="475.10999"
y1="746.39001"
x2="462.42001"
y2="746.39001"
id="line62" />
<line
class="cls-13"
x1="468.76001"
y1="740.03998"
x2="468.76001"
y2="752.72998"
id="line63" />
<circle
class="cls-5"
cx="711.06"
cy="746.5"
r="20.200001"
id="circle63" />
<circle
class="cls-2"
cx="711.06"
cy="746.5"
r="11.67"
id="circle64" />
<circle
class="cls-14"
cx="711.06"
cy="746.5"
r="9.3299999"
id="circle65" />
<circle
class="cls-13"
cx="711.06"
cy="746.5"
r="20.200001"
id="circle66" />
<circle
class="cls-13"
cx="711.06"
cy="746.5"
r="9.3299999"
id="circle67" />
<circle
class="cls-13"
cx="711.06"
cy="746.5"
r="11.67"
id="circle68" />
<line
class="cls-13"
x1="717.40997"
y1="746.39001"
x2="704.71002"
y2="746.39001"
id="line68" />
<line
class="cls-13"
x1="711.06"
y1="740.03998"
x2="711.06"
y2="752.72998"
id="line69" />
<circle
class="cls-5"
cx="468.76001"
cy="1012.62"
r="20.200001"
id="circle69" />
<circle
class="cls-2"
cx="468.76001"
cy="1012.62"
r="11.67"
id="circle70" />
<circle
class="cls-14"
cx="468.76001"
cy="1012.62"
r="9.3299999"
id="circle71" />
<circle
class="cls-13"
cx="468.76001"
cy="1012.62"
r="20.200001"
id="circle72" />
<circle
class="cls-13"
cx="468.76001"
cy="1012.62"
r="9.3299999"
id="circle73" />
<circle
class="cls-13"
cx="468.76001"
cy="1012.62"
r="11.67"
id="circle74" />
<line
class="cls-13"
x1="475.10999"
y1="1012.51"
x2="462.42001"
y2="1012.51"
id="line74" />
<line
class="cls-13"
x1="468.76001"
y1="1006.16"
x2="468.76001"
y2="1018.85"
id="line75" />
<circle
class="cls-5"
cx="711.06"
cy="1012.62"
r="20.200001"
id="circle75" />
<circle
class="cls-2"
cx="711.06"
cy="1012.62"
r="11.67"
id="circle76" />
<circle
class="cls-14"
cx="711.06"
cy="1012.62"
r="9.3299999"
id="circle77" />
<circle
class="cls-13"
cx="711.06"
cy="1012.62"
r="20.200001"
id="circle78" />
<circle
class="cls-13"
cx="711.06"
cy="1012.62"
r="9.3299999"
id="circle79" />
<circle
class="cls-13"
cx="711.06"
cy="1012.62"
r="11.67"
id="circle80" />
<line
class="cls-13"
x1="717.40997"
y1="1012.51"
x2="704.71002"
y2="1012.51"
id="line80" />
<line
class="cls-13"
x1="711.06"
y1="1006.16"
x2="711.06"
y2="1018.85"
id="line81" />
<circle
class="cls-13"
cx="628.69"
cy="926.5"
r="18.08"
id="circle81" />
<circle
class="cls-13"
cx="628.69"
cy="926.5"
r="14.31"
id="circle82" />
<circle
class="cls-13"
cx="691.77002"
cy="926.5"
r="25.82"
id="circle83" />
<circle
class="cls-13"
cx="691.77002"
cy="926.5"
r="21.16"
id="circle84" />
<rect
class="cls-13"
x="506.41"
y="970.47998"
width="170.03"
height="9.9700003"
rx="4.98"
id="rect84" />
<rect
class="cls-13"
x="506.41"
y="1004.91"
width="170.03"
height="9.9700003"
rx="4.98"
id="rect85" />
<path
class="cls-13"
d="m 573.49,709.25 h -4.44 a 3.53,3.53 0 0 1 -2.47,-1 l -42.93,-42.7 a 1.17,1.17 0 0 1 0.83,-2 h 4.43 a 3.5,3.5 0 0 1 2.48,1 l 42.93,42.7 a 1.18,1.18 0 0 1 -0.83,2 z"
id="path85" />
<path
class="cls-13"
d="m 546.14,709.25 h -4.44 a 3.5,3.5 0 0 1 -2.47,-1 l -42.94,-42.7 a 1.18,1.18 0 0 1 0.83,-2 h 4.44 a 3.49,3.49 0 0 1 2.47,1 L 547,707.24 a 1.17,1.17 0 0 1 -0.86,2.01 z"
id="path86" />
<path
class="cls-13"
d="m 518.78,709.25 h -4.44 a 3.53,3.53 0 0 1 -2.47,-1 l -42.93,-42.7 a 1.17,1.17 0 0 1 0.82,-2 h 4.44 a 3.45,3.45 0 0 1 2.47,1 l 42.94,42.7 a 1.18,1.18 0 0 1 -0.83,2 z"
id="path87" />
<path
class="cls-13"
d="m 486.12,709.26 h -4.44 a 3.5,3.5 0 0 1 -2.47,-1 l -7,-7 a 1.17,1.17 0 0 1 0.83,-2 h 4.44 a 3.49,3.49 0 0 1 2.47,1 l 7,7 a 1.18,1.18 0 0 1 -0.83,2 z"
id="path88" />
<path
class="cls-10"
d="m 437.14,874.52 -3.33,-3.33 a 7,7 0 0 1 -2,-5 V 764.1 a 7,7 0 0 1 2,-4.94 l 3.33,-3.33"
id="path89" />
<path
class="cls-10"
d="m 431.77,774.2 v 0 a 2.7,2.7 0 0 1 2.69,2.69 v 19.68 a 2.69,2.69 0 0 1 -2.69,2.69 v 0"
id="path90" />
<path
class="cls-10"
d="m 431.77,836.51 v 0 a 2.69,2.69 0 0 1 2.69,2.69 v 19.68 a 2.7,2.7 0 0 1 -2.69,2.69 v 0"
id="path91" />
<path
class="cls-2"
d="M 510.56,867.89 482.76,907 a 5.28,5.28 0 0 1 -7.37,1.24 v 0 a 5.28,5.28 0 0 1 -1.23,-7.36 L 502,861.77 a 5.27,5.27 0 0 1 7.36,-1.24 v 0 a 5.28,5.28 0 0 1 1.2,7.36 z"
id="path92" />
<path
class="cls-2"
d="m 568.87,908.2 v 0 a 5.3,5.3 0 0 1 -7.38,-1.24 l -24,-33.77 -24,33.77 a 5.29,5.29 0 0 1 -7.37,1.24 v 0 a 5.29,5.29 0 0 1 -1.23,-7.37 l 26.11,-36.76 2.18,-3.06 a 5.28,5.28 0 0 1 8.61,0 l 2.18,3.06 26.16,36.76 a 5.3,5.3 0 0 1 -1.26,7.37 z"
id="path93" />
<path
class="cls-2"
d="m 474.7,958.47 a 2.76,2.76 0 0 1 -2.76,-2.76 V 932.77 A 2.77,2.77 0 0 1 474.7,930 h 7.5 a 9.75,9.75 0 0 1 9.74,9.75 9.75,9.75 0 0 1 -9.74,9.74 h -4.73 v 6.22 a 2.76,2.76 0 0 1 -2.77,2.76 z M 477.47,944 h 4.73 a 4.12,4.12 0 0 0 3,-1.22 4.21,4.21 0 0 0 -3,-7.21 h -4.73 z"
id="path94" />
<path
class="cls-2"
d="m 551.49,954.15 -4.7,-6.86 a 8.68,8.68 0 0 0 0.7,-0.65 9.62,9.62 0 0 0 2.86,-6.89 9.75,9.75 0 0 0 -9.74,-9.75 h -7.49 a 2.77,2.77 0 0 0 -2.76,2.77 v 22.94 a 2.76,2.76 0 0 0 5.52,0 v -6.22 h 4.73 c 0.32,0 0.64,0 0.95,0 l 5.36,7.82 a 2.75,2.75 0 0 0 2.29,1.2 2.76,2.76 0 0 0 2.28,-4.32 z m -15.61,-18.62 h 4.73 a 4.23,4.23 0 0 1 4.21,4.22 4.22,4.22 0 0 1 -4.21,4.21 h -4.73 z"
id="path95" />
<path
class="cls-2"
d="m 518.81,958.47 a 2.75,2.75 0 0 1 -2.5,-1.6 l -5.83,-12.47 -5.82,12.47 a 2.76,2.76 0 0 1 -5.14,-0.34 l -7.16,-22.94 a 2.77,2.77 0 0 1 5.28,-1.65 l 5,16.13 4.56,-9.77 a 3.59,3.59 0 0 1 6.51,0 l 4.56,9.77 5,-16.13 a 2.761132,2.761132 0 1 1 5.27,1.65 l -7.15,22.94 a 2.78,2.78 0 0 1 -2.46,1.93 z"
id="path96" />
<path
class="cls-2"
d="m 562.65,958.47 h -6.13 a 2.77,2.77 0 0 1 -2.77,-2.77 v -22.93 a 2.77,2.77 0 0 1 2.77,-2.77 h 6.13 a 14.24,14.24 0 0 1 0,28.47 z m -3.37,-5.53 h 3.37 a 8.71,8.71 0 0 0 0,-17.41 h -3.37 z"
id="path97" />
<path
class="cls-1"
d="m 348.12645,153.62741 363.94,355.78 -18.09,16.92 a 24.62,24.62 0 0 1 -33.69,0 l -14.9,-14 a 0.44,0.44 0 0 0 -0.5,-0.09 v 0 a 13.35,13.35 0 0 1 -14.85,-2.89 l -316,-321.58 a 24.13,24.13 0 0 1 1.18,-35 v 0 a 24.13,24.13 0 0 1 32.91,0.86 z"
id="path14"
style="fill:#353535" />
<path
class="cls-2"
d="m 650.4474,514.85692 c 0,0 -2.75,-3.82 -4.5,-3.27 -1.75,0.55 -3.94,0.3 -1.53,2.72 l 12.9,13 c 0,0 0.75,0.76 0.61,3.43 -0.14,2.67 0.07,5.63 0.65,5.91 0.58,0.28 30.43,1.59 30.64,-0.79 0.21,-2.38 0.67,-6.25 -0.74,-5.68 -1.41,0.57 -11.35,4.43 -19.55,0.05 -8.2,-4.38 -18.48,-15.37 -18.48,-15.37 z"
id="path4-6"
style="fill:#1d1e1e" />
<line
class="cls-5"
x1="658.21747"
y1="537.27698"
x2="658.21747"
y2="522.17694"
id="line13"
style="fill:none;stroke:#050606;stroke-width:2px;stroke-miterlimit:10" />
<line
class="cls-5"
x1="689.76752"
y1="537.27698"
x2="689.76752"
y2="529.55695"
id="line14"
style="fill:none;stroke:#050606;stroke-width:2px;stroke-miterlimit:10" />
<line
class="cls-5"
x1="642.55743"
y1="512.43695"
x2="658.21747"
y2="528.21698"
id="line16"
style="fill:none;stroke:#050606;stroke-width:2px;stroke-miterlimit:10" />
<circle
class="cls-2"
cx="676.3775"
cy="506.29691"
r="6"
id="circle16-0"
style="fill:#1d1e1e" />
<circle
class="cls-5"
cx="676.3775"
cy="506.29691"
r="6"
id="circle17-3"
style="fill:none;stroke:#050606;stroke-width:2px;stroke-miterlimit:10" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 21 KiB

View file

@ -78,7 +78,7 @@ enum UserTrackingModes: Int, CaseIterable, Identifiable {
var description: String {
switch self {
case .none:
return "None".localized
return "map.usertrackingmode.none".localized
case .follow:
return "Follow".localized
case .followWithHeading:

View file

@ -17,6 +17,7 @@ enum RegionCodes: Int, CaseIterable, Identifiable {
case cn = 4
case jp = 5
case anz = 6
case anz433 = 22
case kr = 7
case tw = 8
case ru = 9
@ -31,6 +32,8 @@ enum RegionCodes: Int, CaseIterable, Identifiable {
case ph433 = 19
case ph868 = 20
case ph915 = 21
case kz433 = 23
case kz863 = 24
case lora24 = 13
var topic: String {
switch self {
@ -48,6 +51,8 @@ enum RegionCodes: Int, CaseIterable, Identifiable {
"JP"
case .anz:
"ANZ"
case .anz433:
"ANZ_433"
case .kr:
"KR"
case .tw:
@ -76,6 +81,10 @@ enum RegionCodes: Int, CaseIterable, Identifiable {
"ph_868"
case .ph915:
"ph_915"
case .kz433:
"KZ_433"
case .kz863:
"KZ_863"
case .lora24:
"LORA_24"
} }
@ -96,6 +105,8 @@ enum RegionCodes: Int, CaseIterable, Identifiable {
return "Japan".localized
case .anz:
return "Australia / New Zealand".localized
case .anz433:
return "Australia / New Zealand 433MHz".localized
case .kr:
return "Korea".localized
case .tw:
@ -112,8 +123,6 @@ enum RegionCodes: Int, CaseIterable, Identifiable {
return "Ukraine 433MHz".localized
case .ua868:
return "Ukraine 868MHz".localized
case .lora24:
return "2.4 Ghz".localized
case .my433:
return "Malaysia 433MHz".localized
case .my919:
@ -126,6 +135,12 @@ enum RegionCodes: Int, CaseIterable, Identifiable {
return "Philippines 868MHz".localized
case .ph915:
return "Philippines 915MHz".localized
case .kz433:
return "Kazakhstan 433MHz".localized
case .kz863:
return "Kazakhstan 863MHz".localized
case .lora24:
return "2.4 Ghz".localized
}
}
var dutyCycle: Int {
@ -174,6 +189,12 @@ enum RegionCodes: Int, CaseIterable, Identifiable {
return 100
case .ph915:
return 100
case .anz433:
return 100
case .kz433:
return 100
case .kz863:
return 100
}
}
var isCountry: Bool {
@ -222,6 +243,12 @@ enum RegionCodes: Int, CaseIterable, Identifiable {
return true
case .ph915:
return true
case .anz433:
return false
case .kz433:
return true
case .kz863:
return true
}
}
func protoEnumValue() -> Config.LoRaConfig.RegionCode {
@ -271,6 +298,12 @@ enum RegionCodes: Int, CaseIterable, Identifiable {
return Config.LoRaConfig.RegionCode.ph868
case .ph915:
return Config.LoRaConfig.RegionCode.ph915
case .anz433:
return Config.LoRaConfig.RegionCode.anz433
case .kz433:
return Config.LoRaConfig.RegionCode.kz433
case .kz863:
return Config.LoRaConfig.RegionCode.kz863
}
}
}

View file

@ -84,6 +84,10 @@ extension UserEntity {
return "SEEEDXIAOS3"
case "WIOWM1110":
return "WIOWM1110"
case "SEEEDSOLARNODE":
return "SEEEDSOLARNODE"
case "SEEEDWIOTRACKERL1":
return "SEEEDWIOTRACKERL1"
/// RAK Wireless
case "RAK4631":
return "RAK4631"

View file

@ -74,6 +74,8 @@ extension UserDefaults {
case environmentEnableWeatherKit
case enableAdministration
case mapReportingOptIn
case firstLaunch
case showDeviceOnboarding
case usageDataAndCrashReporting
case testIntEnum
}
@ -159,6 +161,11 @@ extension UserDefaults {
@UserDefault(.usageDataAndCrashReporting, defaultValue: true)
static var usageDataAndCrashReporting: Bool
@UserDefault(.firstLaunch, defaultValue: true)
static var firstLaunch: Bool
@UserDefault(.showDeviceOnboarding, defaultValue: false)
static var showDeviceOnboarding: Bool
@UserDefault(.testIntEnum, defaultValue: .one)
static var testIntEnum: TestIntEnum

View file

@ -60,9 +60,9 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
let NONCE_ONLY_DB = 69421
private var isWaitingForWantConfigResponse = false
private var wantConfigTimer: Timer?
private var wantConfigRetryCount = 0
private let maxWantConfigRetries = 6
private var wantConfigTimer: Timer?
private var wantConfigRetryCount = 0
private let maxWantConfigRetries = 6
private let wantConfigTimeoutInterval: TimeInterval = 6.0
// MARK: init
@ -799,33 +799,42 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
}
}
}
guard let cp = connectedPeripheral else {
return
}
// Channels
if decodedInfo.channel.isInitialized && connectedPeripheral != nil {
if decodedInfo.channel.isInitialized {
nowKnown = true
channelPacket(channel: decodedInfo.channel, fromNum: Int64(truncatingIfNeeded: connectedPeripheral.num), context: context)
channelPacket(channel: decodedInfo.channel, fromNum: Int64(truncatingIfNeeded: cp.num), context: context)
}
// Config
if decodedInfo.config.isInitialized && !invalidVersion && connectedPeripheral != nil && self.connectedPeripheral?.num != 0 {
if decodedInfo.config.isInitialized && !invalidVersion && cp.num != 0 {
nowKnown = true
localConfig(config: decodedInfo.config, context: context, nodeNum: Int64(truncatingIfNeeded: self.connectedPeripheral.num), nodeLongName: self.connectedPeripheral?.longName ?? "Unknown")
localConfig(config: decodedInfo.config, context: context, nodeNum: Int64(truncatingIfNeeded: cp.num), nodeLongName: cp.longName)
}
// Module Config
if decodedInfo.moduleConfig.isInitialized && !invalidVersion && self.connectedPeripheral?.num != 0 {
if decodedInfo.moduleConfig.isInitialized && !invalidVersion && cp.num != 0 {
onWantConfigResponseReceived()
nowKnown = true
moduleConfig(config: decodedInfo.moduleConfig, context: context, nodeNum: Int64(truncatingIfNeeded: self.connectedPeripheral.num), nodeLongName: self.connectedPeripheral?.longName ?? "Unknown")
moduleConfig(config: decodedInfo.moduleConfig, context: context, nodeNum: Int64(truncatingIfNeeded: cp.num), nodeLongName: cp.longName)
if decodedInfo.moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.cannedMessage(decodedInfo.moduleConfig.cannedMessage) {
if decodedInfo.moduleConfig.cannedMessage.enabled {
if let connectedNum = self.connectedPeripheral?.num, connectedNum > 0 {
_ = self.getCannedMessageModuleMessages(destNum: connectedNum, wantResponse: true)
}
_ = self.getCannedMessageModuleMessages(destNum: cp.num, wantResponse: true)
}
}
if decodedInfo.config.payloadVariant == Config.OneOf_PayloadVariant.device(decodedInfo.config.device) {
var dc = decodedInfo.config.device
if dc.tzdef.isEmpty {
dc.tzdef = TimeZone.current.posixDescription
_ = self.saveTimeZone(config: dc, user: cp.num)
}
}
}
// Device Metadata
if decodedInfo.metadata.firmwareVersion.count > 0 && !invalidVersion {
nowKnown = true
deviceMetadataPacket(metadata: decodedInfo.metadata, fromNum: connectedPeripheral.num, context: context)
deviceMetadataPacket(metadata: decodedInfo.metadata, fromNum: cp.num, context: context)
connectedPeripheral.firmwareVersion = decodedInfo.metadata.firmwareVersion
let lastDotIndex = decodedInfo.metadata.firmwareVersion.lastIndex(of: ".")
if lastDotIndex == nil {
@ -1084,6 +1093,8 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
Logger.mesh.info("🕸️ MESH PACKET received for Reticulum Tunnel App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure", privacy: .public)")
case .keyVerificationApp:
Logger.mesh.warning("🕸️ MESH PACKET received for Key Verification App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure", privacy: .public)")
case .cayenneApp:
Logger.mesh.info("🕸️ MESH PACKET received Cayenne App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure", privacy: .public)")
}
if decodedInfo.configCompleteID != 0 && decodedInfo.configCompleteID == NONCE_ONLY_CONFIG {
@ -1092,6 +1103,9 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
isSubscribed = true
allowDisconnect = true
Logger.mesh.info("🤜 [BLE] Want Config Complete. ID:\(decodedInfo.configCompleteID, privacy: .public)")
if UserDefaults.firstLaunch {
UserDefaults.showDeviceOnboarding = true
}
if sendTime() {
}
peripherals.removeAll(where: { $0.peripheral.state == CBPeripheralState.disconnected })
@ -2231,6 +2245,29 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
}
return 0
}
public func saveTimeZone(config: Config.DeviceConfig, user: Int64) -> Int64 {
var adminPacket = AdminMessage()
adminPacket.setConfig.device = config
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(user)
meshPacket.from = UInt32(user)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
var dataMessage = DataMessage()
guard let adminData: Data = try? adminPacket.serializedData() else {
return 0
}
dataMessage.payload = adminData
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "⌚ Device Config timezone was empty set timezone to \(config.tzdef)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
return Int64(meshPacket.id)
}
return 0
}
public func saveDisplayConfig(config: Config.DisplayConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 {
var adminPacket = AdminMessage()

View file

@ -1,76 +0,0 @@
import Foundation
import CoreLocation
import MapKit
import OSLog
class LocationHelper: NSObject, ObservableObject, CLLocationManagerDelegate {
static let shared = LocationHelper()
var locationManager = CLLocationManager()
// @Published var region = MKCoordinateRegion()
@Published var authorizationStatus: CLAuthorizationStatus?
override init() {
super.init()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.pausesLocationUpdatesAutomatically = true
locationManager.allowsBackgroundLocationUpdates = true
locationManager.activityType = .other
}
// Apple Park
static let DefaultLocation = CLLocationCoordinate2D(latitude: 37.3346, longitude: -122.0090)
static var currentLocation: CLLocationCoordinate2D {
guard let location = shared.locationManager.location else {
return DefaultLocation
}
return location.coordinate
}
static var satsInView: Int {
// If we have a position we have a sat
var sats = 1
if shared.locationManager.location?.verticalAccuracy ?? 0 > 0 {
sats = 4
if 0...5 ~= shared.locationManager.location?.horizontalAccuracy ?? 0 {
sats = 12
} else if 6...15 ~= shared.locationManager.location?.horizontalAccuracy ?? 0 {
sats = 10
} else if 16...30 ~= shared.locationManager.location?.horizontalAccuracy ?? 0 {
sats = 9
} else if 31...45 ~= shared.locationManager.location?.horizontalAccuracy ?? 0 {
sats = 7
} else if 46...60 ~= shared.locationManager.location?.horizontalAccuracy ?? 0 {
sats = 5
}
} else if shared.locationManager.location?.verticalAccuracy ?? 0 < 0 && 60...300 ~= shared.locationManager.location?.horizontalAccuracy ?? 0 {
sats = 3
} else if shared.locationManager.location?.verticalAccuracy ?? 0 < 0 && shared.locationManager.location?.horizontalAccuracy ?? 0 > 300 {
sats = 2
}
return sats
}
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
switch manager.authorizationStatus {
case .authorizedAlways:
authorizationStatus = .authorizedAlways
case .authorizedWhenInUse:
authorizationStatus = .authorizedWhenInUse
locationManager.requestLocation()
case .restricted:
authorizationStatus = .restricted
case .denied:
authorizationStatus = .denied
case .notDetermined:
authorizationStatus = .notDetermined
locationManager.requestAlwaysAuthorization()
default:
break
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
Logger.services.error("Location manager error: \(error.localizedDescription, privacy: .public)")
}
}

View file

@ -10,14 +10,14 @@ import CoreLocation
import OSLog
// Shared state that manages the `CLLocationManager` and `CLBackgroundActivitySession`.
@MainActor class LocationsHandler: ObservableObject {
@MainActor class LocationsHandler: NSObject, ObservableObject, @preconcurrency CLLocationManagerDelegate {
static let shared = LocationsHandler() // Create a single, shared instance of the object.
private let manager: CLLocationManager
public var manager = CLLocationManager()
private var background: CLBackgroundActivitySession?
var enableSmartPosition: Bool = UserDefaults.enableSmartPosition
@Published var locationsArray: [CLLocation]
@Published var locationsArray: [CLLocation] = [CLLocation]()
@Published var isStationary = false
@Published var count = 0
@Published var isRecording = false
@ -38,16 +38,29 @@ import OSLog
UserDefaults.standard.set(backgroundActivity, forKey: "BGActivitySessionStarted")
}
}
// The continuation we will use to asynchronously ask the user permission to track their location.
private var permissionContinuation: CheckedContinuation<CLAuthorizationStatus, Never>?
func requestLocationAlwaysPermissions() async -> CLAuthorizationStatus {
return await withCheckedContinuation { continuation in
self.permissionContinuation = continuation
manager.requestAlwaysAuthorization()
}
}
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
// This is the line you need to add
permissionContinuation?.resume(returning: manager.authorizationStatus)
}
private init() {
self.manager = CLLocationManager() // Creating a location manager instance is safe to call here in `MainActor`.
override init() {
super.init()
self.manager.delegate = self
self.manager.allowsBackgroundLocationUpdates = true
locationsArray = [CLLocation]()
}
func startLocationUpdates() {
if self.manager.authorizationStatus == .notDetermined {
self.manager.requestWhenInUseAuthorization()
let status = self.manager.authorizationStatus
guard status == .authorizedAlways || status == .authorizedWhenInUse else {
return
}
Logger.services.info("📍 [App] Starting location updates")
Task {

View file

@ -1027,7 +1027,7 @@ func textMessageAppPacket(
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "?")",
content: messageText!,
target: "messages",
path: "meshtastic:///messages?userNum=\(newMessage.fromUser?.num ?? 0)&messageId=\(newMessage.messageId)",
path: "meshtastic:///messages?userNum=\(newMessage.fromUser?.num ?? 0)&messageId=\(newMessage.isEmoji ? newMessage.replyID : newMessage.messageId)",
messageId: newMessage.messageId,
channel: newMessage.channel,
userNum: Int64(packet.from),
@ -1058,7 +1058,7 @@ func textMessageAppPacket(
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "?")",
content: messageText!,
target: "messages",
path: "meshtastic:///messages?channelId=\(newMessage.channel)&messageId=\(newMessage.messageId)",
path: "meshtastic:///messages?channelId=\(newMessage.channel)&messageId=\(newMessage.isEmoji ? newMessage.replyID : newMessage.messageId)",
messageId: newMessage.messageId,
channel: newMessage.channel,
userNum: Int64(newMessage.fromUser?.userId ?? "0"),

View file

@ -8,6 +8,8 @@ import MeshtasticProtobufs
import DatadogCore
import DatadogCrashReporting
import DatadogRUM
import DatadogTrace
import DatadogLogs
@main
struct MeshtasticAppleApp: App {
@ -41,14 +43,24 @@ struct MeshtasticAppleApp: App {
env: environment,
site: .us5
),
trackingConsent: UserDefaults.usageDataAndCrashReporting ? .granted : .notGranted
trackingConsent: UserDefaults.usageDataAndCrashReporting ? .granted : .notGranted,
)
DatadogCrashReporting.CrashReporting.enable()
Logs.enable()
Trace.enable(
with: Trace.Configuration(
sampleRate: 100, networkInfoEnabled: true // 100% sampling for development/testing, reduce for production
)
)
RUM.enable(
with: RUM.Configuration(
applicationID: appID,
uiKitViewsPredicate: DefaultUIKitRUMViewsPredicate(),
uiKitActionsPredicate: DefaultUIKitRUMActionsPredicate()
uiKitActionsPredicate: DefaultUIKitRUMActionsPredicate(),
trackBackgroundEvents: true
)
)
self._appState = ObservedObject(wrappedValue: appState)

View file

@ -8,19 +8,6 @@ import CoreData
import MeshtasticProtobufs
import OSLog
// MARK: - Safe Conversion Helpers
private func safeInt32(from value: UInt32) -> Int32 {
return Int32(clamping: value)
}
private func safeInt32(from value: Int) -> Int32 {
return Int32(clamping: value)
}
private func safeInt32(from value: UInt64) -> Int32 {
return Int32(clamping: value)
}
public func clearStaleNodes(nodeExpireDays: Int, context: NSManagedObjectContext) -> Bool {
var nodeExpireTime: TimeInterval {
return TimeInterval(-nodeExpireDays * 86400)
@ -807,32 +794,32 @@ func upsertPositionConfigPacket(config: Config.PositionConfig, nodeNum: Int64, s
let newPositionConfig = PositionConfigEntity(context: context)
newPositionConfig.smartPositionEnabled = config.positionBroadcastSmartEnabled
newPositionConfig.deviceGpsEnabled = config.gpsEnabled
newPositionConfig.gpsMode = Int32(config.gpsMode.rawValue)
newPositionConfig.rxGpio = Int32(config.rxGpio)
newPositionConfig.txGpio = Int32(config.txGpio)
newPositionConfig.gpsEnGpio = Int32(config.gpsEnGpio)
newPositionConfig.gpsMode = Int32(truncatingIfNeeded: config.gpsMode.rawValue)
newPositionConfig.rxGpio = Int32(truncatingIfNeeded: config.rxGpio)
newPositionConfig.txGpio = Int32(truncatingIfNeeded: config.txGpio)
newPositionConfig.gpsEnGpio = Int32(truncatingIfNeeded: config.gpsEnGpio)
newPositionConfig.fixedPosition = config.fixedPosition
newPositionConfig.positionBroadcastSeconds = Int32(truncatingIfNeeded: config.positionBroadcastSecs)
newPositionConfig.broadcastSmartMinimumIntervalSecs = Int32(config.broadcastSmartMinimumIntervalSecs)
newPositionConfig.broadcastSmartMinimumDistance = Int32(config.broadcastSmartMinimumDistance)
newPositionConfig.positionFlags = Int32(config.positionFlags)
newPositionConfig.broadcastSmartMinimumIntervalSecs = Int32(truncatingIfNeeded: config.broadcastSmartMinimumIntervalSecs)
newPositionConfig.broadcastSmartMinimumDistance = Int32(truncatingIfNeeded: config.broadcastSmartMinimumDistance)
newPositionConfig.positionFlags = Int32(truncatingIfNeeded: config.positionFlags)
newPositionConfig.gpsAttemptTime = 900
newPositionConfig.gpsUpdateInterval = Int32(config.gpsUpdateInterval)
newPositionConfig.gpsUpdateInterval = Int32(truncatingIfNeeded: config.gpsUpdateInterval)
fetchedNode[0].positionConfig = newPositionConfig
} else {
fetchedNode[0].positionConfig?.smartPositionEnabled = config.positionBroadcastSmartEnabled
fetchedNode[0].positionConfig?.deviceGpsEnabled = config.gpsEnabled
fetchedNode[0].positionConfig?.gpsMode = Int32(config.gpsMode.rawValue)
fetchedNode[0].positionConfig?.rxGpio = Int32(config.rxGpio)
fetchedNode[0].positionConfig?.txGpio = Int32(config.txGpio)
fetchedNode[0].positionConfig?.gpsEnGpio = Int32(config.gpsEnGpio)
fetchedNode[0].positionConfig?.gpsMode = Int32(truncatingIfNeeded: config.gpsMode.rawValue)
fetchedNode[0].positionConfig?.rxGpio = Int32(truncatingIfNeeded: config.rxGpio)
fetchedNode[0].positionConfig?.txGpio = Int32(truncatingIfNeeded: config.txGpio)
fetchedNode[0].positionConfig?.gpsEnGpio = Int32(truncatingIfNeeded: config.gpsEnGpio)
fetchedNode[0].positionConfig?.fixedPosition = config.fixedPosition
fetchedNode[0].positionConfig?.positionBroadcastSeconds = Int32(truncatingIfNeeded: config.positionBroadcastSecs)
fetchedNode[0].positionConfig?.broadcastSmartMinimumIntervalSecs = Int32(config.broadcastSmartMinimumIntervalSecs)
fetchedNode[0].positionConfig?.broadcastSmartMinimumDistance = Int32(config.broadcastSmartMinimumDistance)
fetchedNode[0].positionConfig?.broadcastSmartMinimumIntervalSecs = Int32(truncatingIfNeeded: config.broadcastSmartMinimumIntervalSecs)
fetchedNode[0].positionConfig?.broadcastSmartMinimumDistance = Int32(truncatingIfNeeded: config.broadcastSmartMinimumDistance)
fetchedNode[0].positionConfig?.gpsAttemptTime = 900
fetchedNode[0].positionConfig?.gpsUpdateInterval = Int32(config.gpsUpdateInterval)
fetchedNode[0].positionConfig?.positionFlags = Int32(config.positionFlags)
fetchedNode[0].positionConfig?.gpsUpdateInterval = Int32(truncatingIfNeeded: config.gpsUpdateInterval)
fetchedNode[0].positionConfig?.positionFlags = Int32(truncatingIfNeeded: config.positionFlags)
}
if sessionPasskey != nil {
fetchedNode[0].sessionPasskey = sessionPasskey
@ -937,6 +924,8 @@ func upsertSecurityConfigPacket(config: Config.SecurityConfig, nodeNum: Int64, s
fetchedNode[0].securityConfig?.adminKey = config.adminKey[0]
if config.adminKey.count > 1 {
fetchedNode[0].securityConfig?.adminKey2 = config.adminKey[1]
}
if config.adminKey.count > 2 {
fetchedNode[0].securityConfig?.adminKey3 = config.adminKey[2]
}
}
@ -1512,23 +1501,23 @@ func upsertTelemetryModuleConfigPacket(config: ModuleConfig.TelemetryConfig, nod
if !fetchedNode.isEmpty {
if fetchedNode[0].telemetryConfig == nil {
let newTelemetryConfig = TelemetryConfigEntity(context: context)
newTelemetryConfig.deviceUpdateInterval = safeInt32(from: config.deviceUpdateInterval)
newTelemetryConfig.environmentUpdateInterval = safeInt32(from: config.environmentUpdateInterval)
newTelemetryConfig.deviceUpdateInterval = Int32(truncatingIfNeeded: config.deviceUpdateInterval)
newTelemetryConfig.environmentUpdateInterval = Int32(truncatingIfNeeded: config.environmentUpdateInterval)
newTelemetryConfig.environmentMeasurementEnabled = config.environmentMeasurementEnabled
newTelemetryConfig.environmentScreenEnabled = config.environmentScreenEnabled
newTelemetryConfig.environmentDisplayFahrenheit = config.environmentDisplayFahrenheit
newTelemetryConfig.powerMeasurementEnabled = config.powerMeasurementEnabled
newTelemetryConfig.powerUpdateInterval = safeInt32(from: config.powerUpdateInterval)
newTelemetryConfig.powerUpdateInterval = Int32(truncatingIfNeeded: config.powerUpdateInterval)
newTelemetryConfig.powerScreenEnabled = config.powerScreenEnabled
fetchedNode[0].telemetryConfig = newTelemetryConfig
} else {
fetchedNode[0].telemetryConfig?.deviceUpdateInterval = safeInt32(from: config.deviceUpdateInterval)
fetchedNode[0].telemetryConfig?.environmentUpdateInterval = safeInt32(from: config.environmentUpdateInterval)
fetchedNode[0].telemetryConfig?.deviceUpdateInterval = Int32(truncatingIfNeeded: config.deviceUpdateInterval)
fetchedNode[0].telemetryConfig?.environmentUpdateInterval = Int32(truncatingIfNeeded: config.environmentUpdateInterval)
fetchedNode[0].telemetryConfig?.environmentMeasurementEnabled = config.environmentMeasurementEnabled
fetchedNode[0].telemetryConfig?.environmentScreenEnabled = config.environmentScreenEnabled
fetchedNode[0].telemetryConfig?.environmentDisplayFahrenheit = config.environmentDisplayFahrenheit
fetchedNode[0].telemetryConfig?.powerMeasurementEnabled = config.powerMeasurementEnabled
fetchedNode[0].telemetryConfig?.powerUpdateInterval = safeInt32(from: config.powerUpdateInterval)
fetchedNode[0].telemetryConfig?.powerUpdateInterval = Int32(truncatingIfNeeded: config.powerUpdateInterval)
fetchedNode[0].telemetryConfig?.powerScreenEnabled = config.powerScreenEnabled
}
if sessionPasskey != nil {

View file

@ -923,6 +923,20 @@
],
"requiresDfu": true
},
{
"hwModel": 100,
"hwModelSlug": "SEEED_WIO_TRACKER_L1_EINK",
"platformioTarget": "seeed_wio_tracker_L1_eink",
"architecture": "nrf52840",
"activelySupported": false,
"supportLevel": 1,
"displayName": "Seeed Wio Tracker L1 E-Ink",
"tags": [
"Seeed"
],
"requiresDfu": true,
"hasInkHud": true
},
{
"hwModel": 97,
"hwModelSlug": "CROWPANEL",

View file

@ -27,20 +27,6 @@ struct Connect: View {
@State var presentingSwitchPreferredPeripheral = false
@State var selectedPeripherialId = ""
init () {
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.getNotificationSettings(completionHandler: { (settings) in
if settings.authorizationStatus == .notDetermined {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound, .criticalAlert]) { success, error in
if success {
Logger.services.info("Notifications are all set!")
} else if let error = error {
Logger.services.error("\(error.localizedDescription, privacy: .public)")
}
}
}
})
}
var body: some View {
NavigationStack {
VStack {

View file

@ -5,11 +5,10 @@
import SwiftUI
struct ContentView: View {
@ObservedObject
var appState: AppState
@ObservedObject var appState: AppState
@ObservedObject
var router: Router
@ObservedObject var router: Router
@State var isShowingDeviceOnboardingFlow: Bool = false
init(appState: AppState, router: Router) {
self.appState = appState
@ -58,6 +57,21 @@ struct ContentView: View {
.font(.title)
}
.tag(NavigationState.Tab.settings)
}.sheet(
isPresented: $isShowingDeviceOnboardingFlow,
onDismiss: {
UserDefaults.firstLaunch = false
}, content: {
DeviceOnboarding()
}
)
.onAppear {
if UserDefaults.firstLaunch {
isShowingDeviceOnboardingFlow = true
}
}
.onChange(of: UserDefaults.showDeviceOnboarding) { newValue in
isShowingDeviceOnboardingFlow = newValue
}
}
}

View file

@ -284,6 +284,9 @@ struct NodeList: View {
// Make sure the ZStack passes through accessibility to the ConnectedDevice component
.accessibilityElement(children: .contain)
)
.onDisappear {
router.navigationState.nodeListSelectedNodeNum = nil
}
}
} else {
ContentUnavailableView("Select Node", systemImage: "flipphone")

View file

@ -0,0 +1,284 @@
import CoreBluetooth
import OSLog
import SwiftUI
import Foundation
import MapKit
struct DeviceOnboarding: View {
enum SetupGuide: Hashable {
case notifications
case location
}
@EnvironmentObject var bleManager: BLEManager
@State var navigationPath: [SetupGuide] = []
@State var locationStatus = LocationsHandler.shared.manager.authorizationStatus
@Environment(\.dismiss) var dismiss
/// The Title View
var title: some View {
VStack {
Text("Welcome to")
.font(.title2.bold())
.multilineTextAlignment(.center)
.fixedSize(horizontal: false, vertical: true)
Text("Meshtastic")
.font(.largeTitle.bold())
.multilineTextAlignment(.center)
.fixedSize(horizontal: false, vertical: true)
}
}
var welcomeView: some View {
VStack {
ScrollView(.vertical, showsIndicators: false) {
VStack {
// Title
title
.padding(.top)
// Onboarding
VStack(alignment: .leading, spacing: 16) {
makeRow(
icon: "antenna.radiowaves.left.and.right",
title: "Stay Connected Anywhere",
subtitle: "Communicate off-the-grid with your friends and community without cell service."
)
makeRow(
icon: "point.3.connected.trianglepath.dotted",
title: "Create Your Own Networks",
subtitle: "Easily set up private mesh networks for secure and reliable communication in remote areas."
)
makeRow(
icon: "location",
title: "Track and Share Locations",
subtitle: "Share your location in real-time and keep your group coordinated with integrated GPS features."
)
}
.padding()
}
.interactiveDismissDisabled()
}
Spacer()
if bleManager.isSwitchedOn {
Button {
Task {
await goToNextStep(after: nil)
}
} label: {
Text("Get started")
.frame(maxWidth: .infinity)
}
.buttonBorderShape(.capsule)
.controlSize(.large)
.padding()
.buttonStyle(.borderedProminent)
}
}
}
var notificationView: some View {
VStack {
VStack {
Text("App Notifications")
.font(.largeTitle.bold())
.multilineTextAlignment(.center)
.fixedSize(horizontal: false, vertical: true)
}
Spacer()
VStack(alignment: .leading, spacing: 16) {
Text("Send Notifications")
.font(.title2.bold())
.multilineTextAlignment(.center)
.fixedSize(horizontal: false, vertical: true)
makeRow(
icon: "message",
title: "Incoming Messages",
subtitle: "Meshtastic notifications for channel messages and direct messages"
)
makeRow(
icon: "flipphone",
title: "New Nodes",
subtitle: "Allow Meshtastic to send notifications for messages, newly discovered nodes and low battery alerts for the connected device."
)
makeRow(
icon: "battery.25percent",
title: "Low Battery",
subtitle: "Allow Meshtastic to send notifications for messages, newly discovered nodes and low battery alerts for the connected device."
)
Text("Critical Alerts")
.font(.title2.bold())
.multilineTextAlignment(.center)
.fixedSize(horizontal: false, vertical: true)
makeRow(
icon: "exclamationmark.triangle.fill",
subtitle: "Select packets sent as critical will ignore the mute switch and Do Not Disturb settings in the OS notification center."
)
}
.padding()
Spacer()
Button {
Task {
await requestNotificationsPermissions()
await goToNextStep(after: .notifications)
}
} label: {
Text("Configure notification permissions")
.frame(maxWidth: .infinity)
}
.buttonBorderShape(.capsule)
.controlSize(.large)
.padding()
.buttonStyle(.borderedProminent)
}
}
var locationView: some View {
VStack {
VStack {
Text("Phone Location")
.font(.largeTitle.bold())
.multilineTextAlignment(.center)
.fixedSize(horizontal: false, vertical: true)
}
VStack(alignment: .leading, spacing: 16) {
Text("Meshtastic uses your phone's location to enable a number of features. You can update your location permissions at any time from Settings > App Settings > Open Settings.")
.font(.body.bold())
.multilineTextAlignment(.center)
.fixedSize(horizontal: false, vertical: true)
makeRow(
icon: "location",
title: "Share Location",
subtitle: "Use your phone GPS to send locations to your node to instead of using a hardware GPS on your node."
)
makeRow(
icon: "lines.measurement.horizontal",
title: "Distance Measurements",
subtitle: "Used to display the distance between your phone and other Meshtastic nodes where positions are available."
)
makeRow(
icon: "line.3.horizontal.decrease.circle",
title: "Distance Filters",
subtitle: "Filter the node list and mesh map based on proximity to your phone."
)
makeRow(
icon: "mappin",
title: "Mesh Map Location",
subtitle: "Enables the blue location dot for your phone in the mesh map."
)
}
.padding()
Spacer()
Button {
Task {
await requestLocationPermissions()
}
} label: {
Text("Configure Location Permissions")
.frame(maxWidth: .infinity)
}
.padding()
.buttonBorderShape(.capsule)
.controlSize(.large)
.padding()
.buttonStyle(.borderedProminent)
}
}
var body: some View {
NavigationStack(path: $navigationPath) {
welcomeView
.navigationDestination(for: SetupGuide.self) { guide in
switch guide {
case .notifications:
notificationView
case .location:
locationView
}
}
}
.toolbar(.hidden)
}
@ViewBuilder
func makeRow(
icon: String,
title: String = "",
subtitle: String
) -> some View {
HStack(alignment: .center) {
Image(systemName: icon)
.resizable()
.symbolRenderingMode(.multicolor)
.font(.subheadline)
.aspectRatio(contentMode: .fit)
.padding()
.frame(width: 72, height: 72)
VStack(alignment: .leading) {
Text(title)
.font(.subheadline.weight(.semibold))
.foregroundColor(.primary)
.fixedSize(horizontal: false, vertical: true)
Text(subtitle)
.font(.subheadline)
.foregroundColor(.secondary)
.fixedSize(horizontal: false, vertical: true)
}.multilineTextAlignment(.leading)
}.accessibilityElement(children: .combine)
}
// MARK: Navigation
func goToNextStep(after step: SetupGuide?) async {
switch step {
case .none:
let status = await UNUserNotificationCenter.current().notificationSettings().authorizationStatus
let criticalAlert = await UNUserNotificationCenter.current().notificationSettings().criticalAlertSetting
if status == .notDetermined && criticalAlert == .notSupported {
navigationPath.append(.notifications)
} else {
fallthrough
}
case .notifications:
locationStatus = LocationsHandler.shared.manager.authorizationStatus
if locationStatus == .notDetermined || locationStatus == .restricted || locationStatus == .denied {
navigationPath.append(.location)
} else {
fallthrough
}
case .location:
let status = LocationsHandler.shared.manager.authorizationStatus
if status != .notDetermined && status != .restricted && status != .denied {
dismiss()
}
}
}
// MARK: Permission Checks
func requestNotificationsPermissions() async {
let center = UNUserNotificationCenter.current()
do {
let success = try await center.requestAuthorization(options: [.alert, .badge, .sound, .criticalAlert])
if success {
Logger.services.info("Notification permissions are enabled")
} else {
Logger.services.info("Notification permissions denied")
}
} catch {
Logger.services.error("Notification permissions error: \(error.localizedDescription)")
}
}
func requestLocationPermissions() async {
locationStatus = await LocationsHandler.shared.requestLocationAlwaysPermissions()
if locationStatus != .notDetermined {
Logger.services.info("Location permissions are enabled")
} else {
Logger.services.info("Location permissions denied")
}
dismiss()
}
}

View file

@ -334,11 +334,5 @@ struct DeviceConfig: View {
self.tripleClickAsAdHocPing = node?.deviceConfig?.tripleClickAsAdHocPing ?? false
self.ledHeartbeatEnabled = node?.deviceConfig?.ledHeartbeatEnabled ?? true
self.tzdef = node?.deviceConfig?.tzdef ?? ""
if self.tzdef.isEmpty {
self.tzdef = TimeZone.current.posixDescription
self.hasChanges = true
} else {
self.hasChanges = false
}
}
}

View file

@ -436,6 +436,12 @@ public struct Config: Sendable {
/// Non-notification system buzzer tones only.
/// Buzzer is enabled only for non-notification tones such as button presses, startup, shutdown, but not for alerts.
case systemOnly // = 3
///
/// Direct Message notifications only.
/// Buzzer is enabled only for direct messages and alerts, but not for button presses.
/// External notification config determines the specifics of the notification behavior.
case directMsgOnly // = 4
case UNRECOGNIZED(Int)
public init() {
@ -448,6 +454,7 @@ public struct Config: Sendable {
case 1: self = .disabled
case 2: self = .notificationsOnly
case 3: self = .systemOnly
case 4: self = .directMsgOnly
default: self = .UNRECOGNIZED(rawValue)
}
}
@ -458,6 +465,7 @@ public struct Config: Sendable {
case .disabled: return 1
case .notificationsOnly: return 2
case .systemOnly: return 3
case .directMsgOnly: return 4
case .UNRECOGNIZED(let i): return i
}
}
@ -468,6 +476,7 @@ public struct Config: Sendable {
.disabled,
.notificationsOnly,
.systemOnly,
.directMsgOnly,
]
}
@ -1579,6 +1588,18 @@ public struct Config: Sendable {
///
/// Philippines 915mhz
case ph915 // = 21
///
/// Australia / New Zealand 433MHz
case anz433 // = 22
///
/// Kazakhstan 433MHz
case kz433 // = 23
///
/// Kazakhstan 863MHz
case kz863 // = 24
case UNRECOGNIZED(Int)
public init() {
@ -1609,6 +1630,9 @@ public struct Config: Sendable {
case 19: self = .ph433
case 20: self = .ph868
case 21: self = .ph915
case 22: self = .anz433
case 23: self = .kz433
case 24: self = .kz863
default: self = .UNRECOGNIZED(rawValue)
}
}
@ -1637,6 +1661,9 @@ public struct Config: Sendable {
case .ph433: return 19
case .ph868: return 20
case .ph915: return 21
case .anz433: return 22
case .kz433: return 23
case .kz863: return 24
case .UNRECOGNIZED(let i): return i
}
}
@ -1665,6 +1692,9 @@ public struct Config: Sendable {
.ph433,
.ph868,
.ph915,
.anz433,
.kz433,
.kz863,
]
}
@ -2247,6 +2277,7 @@ extension Config.DeviceConfig.BuzzerMode: SwiftProtobuf._ProtoNameProviding {
1: .same(proto: "DISABLED"),
2: .same(proto: "NOTIFICATIONS_ONLY"),
3: .same(proto: "SYSTEM_ONLY"),
4: .same(proto: "DIRECT_MSG_ONLY"),
]
}
@ -2992,6 +3023,9 @@ extension Config.LoRaConfig.RegionCode: SwiftProtobuf._ProtoNameProviding {
19: .same(proto: "PH_433"),
20: .same(proto: "PH_868"),
21: .same(proto: "PH_915"),
22: .same(proto: "ANZ_433"),
23: .same(proto: "KZ_433"),
24: .same(proto: "KZ_863"),
]
}

View file

@ -21,6 +21,53 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
typealias Version = _2
}
public enum CompassMode: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
/// Compass with dynamic ring and heading
case dynamic // = 0
///
/// Compass with fixed ring and heading
case fixedRing // = 1
///
/// Compass with heading and freeze option
case freezeHeading // = 2
case UNRECOGNIZED(Int)
public init() {
self = .dynamic
}
public init?(rawValue: Int) {
switch rawValue {
case 0: self = .dynamic
case 1: self = .fixedRing
case 2: self = .freezeHeading
default: self = .UNRECOGNIZED(rawValue)
}
}
public var rawValue: Int {
switch self {
case .dynamic: return 0
case .fixedRing: return 1
case .freezeHeading: return 2
case .UNRECOGNIZED(let i): return i
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [CompassMode] = [
.dynamic,
.fixedRing,
.freezeHeading,
]
}
public enum Theme: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
@ -350,6 +397,29 @@ public struct DeviceUIConfig: @unchecked Sendable {
/// Clears the value of `mapData`. Subsequent reads from it will return its default value.
public mutating func clearMapData() {_uniqueStorage()._mapData = nil}
///
/// Compass mode
public var compassMode: CompassMode {
get {return _storage._compassMode}
set {_uniqueStorage()._compassMode = newValue}
}
///
/// RGB color for BaseUI
/// 0xRRGGBB format, e.g. 0xFF0000 for red
public var screenRgbColor: UInt32 {
get {return _storage._screenRgbColor}
set {_uniqueStorage()._screenRgbColor = newValue}
}
///
/// Clockface analog style
/// true for analog clockface, false for digital clockface
public var isClockfaceAnalog: Bool {
get {return _storage._isClockfaceAnalog}
set {_uniqueStorage()._isClockfaceAnalog = newValue}
}
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
@ -482,6 +552,14 @@ public struct Map: Sendable {
fileprivate let _protobuf_package = "meshtastic"
extension CompassMode: SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "DYNAMIC"),
1: .same(proto: "FIXED_RING"),
2: .same(proto: "FREEZE_HEADING"),
]
}
extension Theme: SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "DARK"),
@ -533,6 +611,9 @@ extension DeviceUIConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement
13: .standard(proto: "node_highlight"),
14: .standard(proto: "calibration_data"),
15: .standard(proto: "map_data"),
16: .standard(proto: "compass_mode"),
17: .standard(proto: "screen_rgb_color"),
18: .standard(proto: "is_clockface_analog"),
]
fileprivate class _StorageClass {
@ -551,6 +632,9 @@ extension DeviceUIConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement
var _nodeHighlight: NodeHighlight? = nil
var _calibrationData: Data = Data()
var _mapData: Map? = nil
var _compassMode: CompassMode = .dynamic
var _screenRgbColor: UInt32 = 0
var _isClockfaceAnalog: Bool = false
#if swift(>=5.10)
// This property is used as the initial default value for new instances of the type.
@ -580,6 +664,9 @@ extension DeviceUIConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement
_nodeHighlight = source._nodeHighlight
_calibrationData = source._calibrationData
_mapData = source._mapData
_compassMode = source._compassMode
_screenRgbColor = source._screenRgbColor
_isClockfaceAnalog = source._isClockfaceAnalog
}
}
@ -613,6 +700,9 @@ extension DeviceUIConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement
case 13: try { try decoder.decodeSingularMessageField(value: &_storage._nodeHighlight) }()
case 14: try { try decoder.decodeSingularBytesField(value: &_storage._calibrationData) }()
case 15: try { try decoder.decodeSingularMessageField(value: &_storage._mapData) }()
case 16: try { try decoder.decodeSingularEnumField(value: &_storage._compassMode) }()
case 17: try { try decoder.decodeSingularUInt32Field(value: &_storage._screenRgbColor) }()
case 18: try { try decoder.decodeSingularBoolField(value: &_storage._isClockfaceAnalog) }()
default: break
}
}
@ -670,6 +760,15 @@ extension DeviceUIConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement
try { if let v = _storage._mapData {
try visitor.visitSingularMessageField(value: v, fieldNumber: 15)
} }()
if _storage._compassMode != .dynamic {
try visitor.visitSingularEnumField(value: _storage._compassMode, fieldNumber: 16)
}
if _storage._screenRgbColor != 0 {
try visitor.visitSingularUInt32Field(value: _storage._screenRgbColor, fieldNumber: 17)
}
if _storage._isClockfaceAnalog != false {
try visitor.visitSingularBoolField(value: _storage._isClockfaceAnalog, fieldNumber: 18)
}
}
try unknownFields.traverse(visitor: &visitor)
}
@ -694,6 +793,9 @@ extension DeviceUIConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement
if _storage._nodeHighlight != rhs_storage._nodeHighlight {return false}
if _storage._calibrationData != rhs_storage._calibrationData {return false}
if _storage._mapData != rhs_storage._mapData {return false}
if _storage._compassMode != rhs_storage._compassMode {return false}
if _storage._screenRgbColor != rhs_storage._screenRgbColor {return false}
if _storage._isClockfaceAnalog != rhs_storage._isClockfaceAnalog {return false}
return true
}
if !storagesAreEqual {return false}

View file

@ -442,15 +442,15 @@ public enum HardwareModel: SwiftProtobuf.Enum, Swift.CaseIterable {
/// Elecrow CrowPanel Advance models, ESP32-S3 and TFT with SX1262 radio plugin
case crowpanel // = 97
///*
///
/// Lilygo LINK32 board with sensors
case link32 // = 98
///*
///
/// Seeed Tracker L1
case seeedWioTrackerL1 // = 99
///*
///
/// Seeed Tracker L1 EINK driver
case seeedWioTrackerL1Eink // = 100
@ -458,18 +458,30 @@ public enum HardwareModel: SwiftProtobuf.Enum, Swift.CaseIterable {
/// Reserved ID for future and past use
case qwantzTinyArms // = 101
///*
///
/// Lilygo T-Deck Pro
case tDeckPro // = 102
///*
///
/// Lilygo TLora Pager
case tLoraPager // = 103
///*
///
/// GAT562 Mesh Trial Tracker
case gat562MeshTrialTracker // = 104
///
/// RAKwireless WisMesh Tag
case wismeshTag // = 105
///
/// RAKwireless WisBlock Core RAK3312 https://docs.rakwireless.com/product-categories/wisduo/rak3112-module/overview/
case rak3312 // = 106
///
/// Elecrow ThinkNode M5 https://www.elecrow.com/wiki/ThinkNode_M5_Meshtastic_LoRa_Signal_Transceiver_ESP32-S3.html
case thinknodeM5 // = 107
///
/// ------------------------------------------------------------------------------------------------------------------------------------------
/// 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.
@ -588,6 +600,9 @@ public enum HardwareModel: SwiftProtobuf.Enum, Swift.CaseIterable {
case 102: self = .tDeckPro
case 103: self = .tLoraPager
case 104: self = .gat562MeshTrialTracker
case 105: self = .wismeshTag
case 106: self = .rak3312
case 107: self = .thinknodeM5
case 255: self = .privateHw
default: self = .UNRECOGNIZED(rawValue)
}
@ -700,6 +715,9 @@ public enum HardwareModel: SwiftProtobuf.Enum, Swift.CaseIterable {
case .tDeckPro: return 102
case .tLoraPager: return 103
case .gat562MeshTrialTracker: return 104
case .wismeshTag: return 105
case .rak3312: return 106
case .thinknodeM5: return 107
case .privateHw: return 255
case .UNRECOGNIZED(let i): return i
}
@ -812,6 +830,9 @@ public enum HardwareModel: SwiftProtobuf.Enum, Swift.CaseIterable {
.tDeckPro,
.tLoraPager,
.gat562MeshTrialTracker,
.wismeshTag,
.rak3312,
.thinknodeM5,
.privateHw,
]
@ -1059,7 +1080,7 @@ public enum ExcludedModules: SwiftProtobuf.Enum, Swift.CaseIterable {
/// Paxcounter module
case paxcounterConfig // = 4096
///
///
/// Bluetooth config (not technically a module, but used to indicate bluetooth capabilities)
case bluetoothConfig // = 8192
@ -3640,6 +3661,9 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding {
102: .same(proto: "T_DECK_PRO"),
103: .same(proto: "T_LORA_PAGER"),
104: .same(proto: "GAT562_MESH_TRIAL_TRACKER"),
105: .same(proto: "WISMESH_TAG"),
106: .same(proto: "RAK3312"),
107: .same(proto: "THINKNODE_M5"),
255: .same(proto: "PRIVATE_HW"),
]
}

View file

@ -202,6 +202,12 @@ public enum PortNum: SwiftProtobuf.Enum, Swift.CaseIterable {
/// ENCODING: Fragmented RNS Packet. Handled by Meshtastic RNS interface
case reticulumTunnelApp // = 76
///
/// App for transporting Cayenne Low Power Payload, popular for LoRaWAN sensor nodes. Offers ability to send
/// arbitrary telemetry over meshtastic that is not covered by telemetry.proto
/// ENCODING: CayenneLLP
case cayenneApp // = 77
///
/// Private applications should use portnums >= 256.
/// To simplify initial development and testing you can use "PRIVATE_APP"
@ -252,6 +258,7 @@ public enum PortNum: SwiftProtobuf.Enum, Swift.CaseIterable {
case 73: self = .mapReportApp
case 74: self = .powerstressApp
case 76: self = .reticulumTunnelApp
case 77: self = .cayenneApp
case 256: self = .privateApp
case 257: self = .atakForwarder
case 511: self = .max
@ -289,6 +296,7 @@ public enum PortNum: SwiftProtobuf.Enum, Swift.CaseIterable {
case .mapReportApp: return 73
case .powerstressApp: return 74
case .reticulumTunnelApp: return 76
case .cayenneApp: return 77
case .privateApp: return 256
case .atakForwarder: return 257
case .max: return 511
@ -326,6 +334,7 @@ public enum PortNum: SwiftProtobuf.Enum, Swift.CaseIterable {
.mapReportApp,
.powerstressApp,
.reticulumTunnelApp,
.cayenneApp,
.privateApp,
.atakForwarder,
.max,
@ -365,6 +374,7 @@ extension PortNum: SwiftProtobuf._ProtoNameProviding {
73: .same(proto: "MAP_REPORT_APP"),
74: .same(proto: "POWERSTRESS_APP"),
76: .same(proto: "RETICULUM_TUNNEL_APP"),
77: .same(proto: "CAYENNE_APP"),
256: .same(proto: "PRIVATE_APP"),
257: .same(proto: "ATAK_FORWARDER"),
511: .same(proto: "MAX"),

View file

@ -21,7 +21,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
}
/// Note: There are no 'PowerMon' messages normally in use (PowerMons are sent only as structured logs - slogs).
///But we wrap our State enum in this message to effectively nest a namespace (without our linter yelling at us)
/// But we wrap our State enum in this message to effectively nest a namespace (without our linter yelling at us)
public struct PowerMon: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
@ -30,7 +30,7 @@ public struct PowerMon: Sendable {
public var unknownFields = SwiftProtobuf.UnknownStorage()
/// Any significant power changing event in meshtastic should be tagged with a powermon state transition.
///If you are making new meshtastic features feel free to add new entries at the end of this definition.
/// If you are making new meshtastic features feel free to add new entries at the end of this definition.
public enum State: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
case none // = 0
@ -57,8 +57,8 @@ public struct PowerMon: Sendable {
case wifiOn // = 1024
///
///GPS is actively trying to find our location
///See GPSPowerState for more details
/// GPS is actively trying to find our location
/// See GPSPowerState for more details
case gpsActive // = 2048
case UNRECOGNIZED(Int)
@ -143,8 +143,8 @@ public struct PowerStressMessage: Sendable {
///
/// What operation would we like the UUT to perform.
///note: senders should probably set want_response in their request packets, so that they can know when the state
///machine has started processing their request
/// note: senders should probably set want_response in their request packets, so that they can know when the state
/// machine has started processing their request
public enum Opcode: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int

View file

@ -184,6 +184,10 @@ public enum TelemetrySensorType: SwiftProtobuf.Enum, Swift.CaseIterable {
///
/// PCT2075 Temperature Sensor
case pct2075 // = 39
///
/// ADS1X15 ADC
case ads1X15 // = 40
case UNRECOGNIZED(Int)
public init() {
@ -232,6 +236,7 @@ public enum TelemetrySensorType: SwiftProtobuf.Enum, Swift.CaseIterable {
case 37: self = .rak12035
case 38: self = .max17261
case 39: self = .pct2075
case 40: self = .ads1X15
default: self = .UNRECOGNIZED(rawValue)
}
}
@ -278,6 +283,7 @@ public enum TelemetrySensorType: SwiftProtobuf.Enum, Swift.CaseIterable {
case .rak12035: return 37
case .max17261: return 38
case .pct2075: return 39
case .ads1X15: return 40
case .UNRECOGNIZED(let i): return i
}
}
@ -324,6 +330,7 @@ public enum TelemetrySensorType: SwiftProtobuf.Enum, Swift.CaseIterable {
.rak12035,
.max17261,
.pct2075,
.ads1X15,
]
}
@ -732,6 +739,116 @@ public struct PowerMetrics: Sendable {
/// Clears the value of `ch3Current`. Subsequent reads from it will return its default value.
public mutating func clearCh3Current() {self._ch3Current = nil}
///
/// Voltage (Ch4)
public var ch4Voltage: Float {
get {return _ch4Voltage ?? 0}
set {_ch4Voltage = newValue}
}
/// Returns true if `ch4Voltage` has been explicitly set.
public var hasCh4Voltage: Bool {return self._ch4Voltage != nil}
/// Clears the value of `ch4Voltage`. Subsequent reads from it will return its default value.
public mutating func clearCh4Voltage() {self._ch4Voltage = nil}
///
/// Current (Ch4)
public var ch4Current: Float {
get {return _ch4Current ?? 0}
set {_ch4Current = newValue}
}
/// Returns true if `ch4Current` has been explicitly set.
public var hasCh4Current: Bool {return self._ch4Current != nil}
/// Clears the value of `ch4Current`. Subsequent reads from it will return its default value.
public mutating func clearCh4Current() {self._ch4Current = nil}
///
/// Voltage (Ch5)
public var ch5Voltage: Float {
get {return _ch5Voltage ?? 0}
set {_ch5Voltage = newValue}
}
/// Returns true if `ch5Voltage` has been explicitly set.
public var hasCh5Voltage: Bool {return self._ch5Voltage != nil}
/// Clears the value of `ch5Voltage`. Subsequent reads from it will return its default value.
public mutating func clearCh5Voltage() {self._ch5Voltage = nil}
///
/// Current (Ch5)
public var ch5Current: Float {
get {return _ch5Current ?? 0}
set {_ch5Current = newValue}
}
/// Returns true if `ch5Current` has been explicitly set.
public var hasCh5Current: Bool {return self._ch5Current != nil}
/// Clears the value of `ch5Current`. Subsequent reads from it will return its default value.
public mutating func clearCh5Current() {self._ch5Current = nil}
///
/// Voltage (Ch6)
public var ch6Voltage: Float {
get {return _ch6Voltage ?? 0}
set {_ch6Voltage = newValue}
}
/// Returns true if `ch6Voltage` has been explicitly set.
public var hasCh6Voltage: Bool {return self._ch6Voltage != nil}
/// Clears the value of `ch6Voltage`. Subsequent reads from it will return its default value.
public mutating func clearCh6Voltage() {self._ch6Voltage = nil}
///
/// Current (Ch6)
public var ch6Current: Float {
get {return _ch6Current ?? 0}
set {_ch6Current = newValue}
}
/// Returns true if `ch6Current` has been explicitly set.
public var hasCh6Current: Bool {return self._ch6Current != nil}
/// Clears the value of `ch6Current`. Subsequent reads from it will return its default value.
public mutating func clearCh6Current() {self._ch6Current = nil}
///
/// Voltage (Ch7)
public var ch7Voltage: Float {
get {return _ch7Voltage ?? 0}
set {_ch7Voltage = newValue}
}
/// Returns true if `ch7Voltage` has been explicitly set.
public var hasCh7Voltage: Bool {return self._ch7Voltage != nil}
/// Clears the value of `ch7Voltage`. Subsequent reads from it will return its default value.
public mutating func clearCh7Voltage() {self._ch7Voltage = nil}
///
/// Current (Ch7)
public var ch7Current: Float {
get {return _ch7Current ?? 0}
set {_ch7Current = newValue}
}
/// Returns true if `ch7Current` has been explicitly set.
public var hasCh7Current: Bool {return self._ch7Current != nil}
/// Clears the value of `ch7Current`. Subsequent reads from it will return its default value.
public mutating func clearCh7Current() {self._ch7Current = nil}
///
/// Voltage (Ch8)
public var ch8Voltage: Float {
get {return _ch8Voltage ?? 0}
set {_ch8Voltage = newValue}
}
/// Returns true if `ch8Voltage` has been explicitly set.
public var hasCh8Voltage: Bool {return self._ch8Voltage != nil}
/// Clears the value of `ch8Voltage`. Subsequent reads from it will return its default value.
public mutating func clearCh8Voltage() {self._ch8Voltage = nil}
///
/// Current (Ch8)
public var ch8Current: Float {
get {return _ch8Current ?? 0}
set {_ch8Current = newValue}
}
/// Returns true if `ch8Current` has been explicitly set.
public var hasCh8Current: Bool {return self._ch8Current != nil}
/// Clears the value of `ch8Current`. Subsequent reads from it will return its default value.
public mutating func clearCh8Current() {self._ch8Current = nil}
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
@ -742,6 +859,16 @@ public struct PowerMetrics: Sendable {
fileprivate var _ch2Current: Float? = nil
fileprivate var _ch3Voltage: Float? = nil
fileprivate var _ch3Current: Float? = nil
fileprivate var _ch4Voltage: Float? = nil
fileprivate var _ch4Current: Float? = nil
fileprivate var _ch5Voltage: Float? = nil
fileprivate var _ch5Current: Float? = nil
fileprivate var _ch6Voltage: Float? = nil
fileprivate var _ch6Current: Float? = nil
fileprivate var _ch7Voltage: Float? = nil
fileprivate var _ch7Current: Float? = nil
fileprivate var _ch8Voltage: Float? = nil
fileprivate var _ch8Current: Float? = nil
}
///
@ -894,6 +1021,28 @@ public struct AirQualityMetrics: Sendable {
/// Clears the value of `co2`. Subsequent reads from it will return its default value.
public mutating func clearCo2() {self._co2 = nil}
///
/// CO2 sensor temperature in degC
public var co2Temperature: Float {
get {return _co2Temperature ?? 0}
set {_co2Temperature = newValue}
}
/// Returns true if `co2Temperature` has been explicitly set.
public var hasCo2Temperature: Bool {return self._co2Temperature != nil}
/// Clears the value of `co2Temperature`. Subsequent reads from it will return its default value.
public mutating func clearCo2Temperature() {self._co2Temperature = nil}
///
/// CO2 sensor relative humidity in %
public var co2Humidity: Float {
get {return _co2Humidity ?? 0}
set {_co2Humidity = newValue}
}
/// Returns true if `co2Humidity` has been explicitly set.
public var hasCo2Humidity: Bool {return self._co2Humidity != nil}
/// Clears the value of `co2Humidity`. Subsequent reads from it will return its default value.
public mutating func clearCo2Humidity() {self._co2Humidity = nil}
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
@ -911,6 +1060,8 @@ public struct AirQualityMetrics: Sendable {
fileprivate var _particles50Um: UInt32? = nil
fileprivate var _particles100Um: UInt32? = nil
fileprivate var _co2: UInt32? = nil
fileprivate var _co2Temperature: Float? = nil
fileprivate var _co2Humidity: Float? = nil
}
///
@ -1104,85 +1255,91 @@ public struct HostMetrics: Sendable {
///
/// Types of Measurements the telemetry module is equipped to handle
public struct Telemetry: Sendable {
public struct Telemetry: @unchecked Sendable {
// 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.
///
/// Seconds since 1970 - or 0 for unknown/unset
public var time: UInt32 = 0
public var time: UInt32 {
get {return _storage._time}
set {_uniqueStorage()._time = newValue}
}
public var variant: Telemetry.OneOf_Variant? = nil
public var variant: OneOf_Variant? {
get {return _storage._variant}
set {_uniqueStorage()._variant = newValue}
}
///
/// Key native device metrics such as battery level
public var deviceMetrics: DeviceMetrics {
get {
if case .deviceMetrics(let v)? = variant {return v}
if case .deviceMetrics(let v)? = _storage._variant {return v}
return DeviceMetrics()
}
set {variant = .deviceMetrics(newValue)}
set {_uniqueStorage()._variant = .deviceMetrics(newValue)}
}
///
/// Weather station or other environmental metrics
public var environmentMetrics: EnvironmentMetrics {
get {
if case .environmentMetrics(let v)? = variant {return v}
if case .environmentMetrics(let v)? = _storage._variant {return v}
return EnvironmentMetrics()
}
set {variant = .environmentMetrics(newValue)}
set {_uniqueStorage()._variant = .environmentMetrics(newValue)}
}
///
/// Air quality metrics
public var airQualityMetrics: AirQualityMetrics {
get {
if case .airQualityMetrics(let v)? = variant {return v}
if case .airQualityMetrics(let v)? = _storage._variant {return v}
return AirQualityMetrics()
}
set {variant = .airQualityMetrics(newValue)}
set {_uniqueStorage()._variant = .airQualityMetrics(newValue)}
}
///
/// Power Metrics
public var powerMetrics: PowerMetrics {
get {
if case .powerMetrics(let v)? = variant {return v}
if case .powerMetrics(let v)? = _storage._variant {return v}
return PowerMetrics()
}
set {variant = .powerMetrics(newValue)}
set {_uniqueStorage()._variant = .powerMetrics(newValue)}
}
///
/// Local device mesh statistics
public var localStats: LocalStats {
get {
if case .localStats(let v)? = variant {return v}
if case .localStats(let v)? = _storage._variant {return v}
return LocalStats()
}
set {variant = .localStats(newValue)}
set {_uniqueStorage()._variant = .localStats(newValue)}
}
///
/// Health telemetry metrics
public var healthMetrics: HealthMetrics {
get {
if case .healthMetrics(let v)? = variant {return v}
if case .healthMetrics(let v)? = _storage._variant {return v}
return HealthMetrics()
}
set {variant = .healthMetrics(newValue)}
set {_uniqueStorage()._variant = .healthMetrics(newValue)}
}
///
/// Linux host metrics
public var hostMetrics: HostMetrics {
get {
if case .hostMetrics(let v)? = variant {return v}
if case .hostMetrics(let v)? = _storage._variant {return v}
return HostMetrics()
}
set {variant = .hostMetrics(newValue)}
set {_uniqueStorage()._variant = .hostMetrics(newValue)}
}
public var unknownFields = SwiftProtobuf.UnknownStorage()
@ -1213,6 +1370,8 @@ public struct Telemetry: Sendable {
}
public init() {}
fileprivate var _storage = _StorageClass.defaultInstance
}
///
@ -1281,6 +1440,7 @@ extension TelemetrySensorType: SwiftProtobuf._ProtoNameProviding {
37: .same(proto: "RAK12035"),
38: .same(proto: "MAX17261"),
39: .same(proto: "PCT2075"),
40: .same(proto: "ADS1X15"),
]
}
@ -1597,6 +1757,16 @@ extension PowerMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
4: .standard(proto: "ch2_current"),
5: .standard(proto: "ch3_voltage"),
6: .standard(proto: "ch3_current"),
7: .standard(proto: "ch4_voltage"),
8: .standard(proto: "ch4_current"),
9: .standard(proto: "ch5_voltage"),
10: .standard(proto: "ch5_current"),
11: .standard(proto: "ch6_voltage"),
12: .standard(proto: "ch6_current"),
13: .standard(proto: "ch7_voltage"),
14: .standard(proto: "ch7_current"),
15: .standard(proto: "ch8_voltage"),
16: .standard(proto: "ch8_current"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -1611,6 +1781,16 @@ extension PowerMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
case 4: try { try decoder.decodeSingularFloatField(value: &self._ch2Current) }()
case 5: try { try decoder.decodeSingularFloatField(value: &self._ch3Voltage) }()
case 6: try { try decoder.decodeSingularFloatField(value: &self._ch3Current) }()
case 7: try { try decoder.decodeSingularFloatField(value: &self._ch4Voltage) }()
case 8: try { try decoder.decodeSingularFloatField(value: &self._ch4Current) }()
case 9: try { try decoder.decodeSingularFloatField(value: &self._ch5Voltage) }()
case 10: try { try decoder.decodeSingularFloatField(value: &self._ch5Current) }()
case 11: try { try decoder.decodeSingularFloatField(value: &self._ch6Voltage) }()
case 12: try { try decoder.decodeSingularFloatField(value: &self._ch6Current) }()
case 13: try { try decoder.decodeSingularFloatField(value: &self._ch7Voltage) }()
case 14: try { try decoder.decodeSingularFloatField(value: &self._ch7Current) }()
case 15: try { try decoder.decodeSingularFloatField(value: &self._ch8Voltage) }()
case 16: try { try decoder.decodeSingularFloatField(value: &self._ch8Current) }()
default: break
}
}
@ -1639,6 +1819,36 @@ extension PowerMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
try { if let v = self._ch3Current {
try visitor.visitSingularFloatField(value: v, fieldNumber: 6)
} }()
try { if let v = self._ch4Voltage {
try visitor.visitSingularFloatField(value: v, fieldNumber: 7)
} }()
try { if let v = self._ch4Current {
try visitor.visitSingularFloatField(value: v, fieldNumber: 8)
} }()
try { if let v = self._ch5Voltage {
try visitor.visitSingularFloatField(value: v, fieldNumber: 9)
} }()
try { if let v = self._ch5Current {
try visitor.visitSingularFloatField(value: v, fieldNumber: 10)
} }()
try { if let v = self._ch6Voltage {
try visitor.visitSingularFloatField(value: v, fieldNumber: 11)
} }()
try { if let v = self._ch6Current {
try visitor.visitSingularFloatField(value: v, fieldNumber: 12)
} }()
try { if let v = self._ch7Voltage {
try visitor.visitSingularFloatField(value: v, fieldNumber: 13)
} }()
try { if let v = self._ch7Current {
try visitor.visitSingularFloatField(value: v, fieldNumber: 14)
} }()
try { if let v = self._ch8Voltage {
try visitor.visitSingularFloatField(value: v, fieldNumber: 15)
} }()
try { if let v = self._ch8Current {
try visitor.visitSingularFloatField(value: v, fieldNumber: 16)
} }()
try unknownFields.traverse(visitor: &visitor)
}
@ -1649,6 +1859,16 @@ extension PowerMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
if lhs._ch2Current != rhs._ch2Current {return false}
if lhs._ch3Voltage != rhs._ch3Voltage {return false}
if lhs._ch3Current != rhs._ch3Current {return false}
if lhs._ch4Voltage != rhs._ch4Voltage {return false}
if lhs._ch4Current != rhs._ch4Current {return false}
if lhs._ch5Voltage != rhs._ch5Voltage {return false}
if lhs._ch5Current != rhs._ch5Current {return false}
if lhs._ch6Voltage != rhs._ch6Voltage {return false}
if lhs._ch6Current != rhs._ch6Current {return false}
if lhs._ch7Voltage != rhs._ch7Voltage {return false}
if lhs._ch7Current != rhs._ch7Current {return false}
if lhs._ch8Voltage != rhs._ch8Voltage {return false}
if lhs._ch8Current != rhs._ch8Current {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
@ -1670,6 +1890,8 @@ extension AirQualityMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
11: .standard(proto: "particles_50um"),
12: .standard(proto: "particles_100um"),
13: .same(proto: "co2"),
14: .standard(proto: "co2_temperature"),
15: .standard(proto: "co2_humidity"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -1691,6 +1913,8 @@ extension AirQualityMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
case 11: try { try decoder.decodeSingularUInt32Field(value: &self._particles50Um) }()
case 12: try { try decoder.decodeSingularUInt32Field(value: &self._particles100Um) }()
case 13: try { try decoder.decodeSingularUInt32Field(value: &self._co2) }()
case 14: try { try decoder.decodeSingularFloatField(value: &self._co2Temperature) }()
case 15: try { try decoder.decodeSingularFloatField(value: &self._co2Humidity) }()
default: break
}
}
@ -1740,6 +1964,12 @@ extension AirQualityMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
try { if let v = self._co2 {
try visitor.visitSingularUInt32Field(value: v, fieldNumber: 13)
} }()
try { if let v = self._co2Temperature {
try visitor.visitSingularFloatField(value: v, fieldNumber: 14)
} }()
try { if let v = self._co2Humidity {
try visitor.visitSingularFloatField(value: v, fieldNumber: 15)
} }()
try unknownFields.traverse(visitor: &visitor)
}
@ -1757,6 +1987,8 @@ extension AirQualityMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
if lhs._particles50Um != rhs._particles50Um {return false}
if lhs._particles100Um != rhs._particles100Um {return false}
if lhs._co2 != rhs._co2 {return false}
if lhs._co2Temperature != rhs._co2Temperature {return false}
if lhs._co2Humidity != rhs._co2Humidity {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
@ -2011,154 +2243,196 @@ extension Telemetry: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation
8: .standard(proto: "host_metrics"),
]
fileprivate class _StorageClass {
var _time: UInt32 = 0
var _variant: Telemetry.OneOf_Variant?
#if swift(>=5.10)
// This property is used as the initial default value for new instances of the type.
// The type itself is protecting the reference to its storage via CoW semantics.
// This will force a copy to be made of this reference when the first mutation occurs;
// hence, it is safe to mark this as `nonisolated(unsafe)`.
static nonisolated(unsafe) let defaultInstance = _StorageClass()
#else
static let defaultInstance = _StorageClass()
#endif
private init() {}
init(copying source: _StorageClass) {
_time = source._time
_variant = source._variant
}
}
fileprivate mutating func _uniqueStorage() -> _StorageClass {
if !isKnownUniquelyReferenced(&_storage) {
_storage = _StorageClass(copying: _storage)
}
return _storage
}
public 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.decodeSingularFixed32Field(value: &self.time) }()
case 2: try {
var v: DeviceMetrics?
var hadOneofValue = false
if let current = self.variant {
hadOneofValue = true
if case .deviceMetrics(let m) = current {v = m}
_ = _uniqueStorage()
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularFixed32Field(value: &_storage._time) }()
case 2: try {
var v: DeviceMetrics?
var hadOneofValue = false
if let current = _storage._variant {
hadOneofValue = true
if case .deviceMetrics(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
_storage._variant = .deviceMetrics(v)
}
}()
case 3: try {
var v: EnvironmentMetrics?
var hadOneofValue = false
if let current = _storage._variant {
hadOneofValue = true
if case .environmentMetrics(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
_storage._variant = .environmentMetrics(v)
}
}()
case 4: try {
var v: AirQualityMetrics?
var hadOneofValue = false
if let current = _storage._variant {
hadOneofValue = true
if case .airQualityMetrics(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
_storage._variant = .airQualityMetrics(v)
}
}()
case 5: try {
var v: PowerMetrics?
var hadOneofValue = false
if let current = _storage._variant {
hadOneofValue = true
if case .powerMetrics(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
_storage._variant = .powerMetrics(v)
}
}()
case 6: try {
var v: LocalStats?
var hadOneofValue = false
if let current = _storage._variant {
hadOneofValue = true
if case .localStats(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
_storage._variant = .localStats(v)
}
}()
case 7: try {
var v: HealthMetrics?
var hadOneofValue = false
if let current = _storage._variant {
hadOneofValue = true
if case .healthMetrics(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
_storage._variant = .healthMetrics(v)
}
}()
case 8: try {
var v: HostMetrics?
var hadOneofValue = false
if let current = _storage._variant {
hadOneofValue = true
if case .hostMetrics(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
_storage._variant = .hostMetrics(v)
}
}()
default: break
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
self.variant = .deviceMetrics(v)
}
}()
case 3: try {
var v: EnvironmentMetrics?
var hadOneofValue = false
if let current = self.variant {
hadOneofValue = true
if case .environmentMetrics(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
self.variant = .environmentMetrics(v)
}
}()
case 4: try {
var v: AirQualityMetrics?
var hadOneofValue = false
if let current = self.variant {
hadOneofValue = true
if case .airQualityMetrics(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
self.variant = .airQualityMetrics(v)
}
}()
case 5: try {
var v: PowerMetrics?
var hadOneofValue = false
if let current = self.variant {
hadOneofValue = true
if case .powerMetrics(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
self.variant = .powerMetrics(v)
}
}()
case 6: try {
var v: LocalStats?
var hadOneofValue = false
if let current = self.variant {
hadOneofValue = true
if case .localStats(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
self.variant = .localStats(v)
}
}()
case 7: try {
var v: HealthMetrics?
var hadOneofValue = false
if let current = self.variant {
hadOneofValue = true
if case .healthMetrics(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
self.variant = .healthMetrics(v)
}
}()
case 8: try {
var v: HostMetrics?
var hadOneofValue = false
if let current = self.variant {
hadOneofValue = true
if case .hostMetrics(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
self.variant = .hostMetrics(v)
}
}()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
if self.time != 0 {
try visitor.visitSingularFixed32Field(value: self.time, fieldNumber: 1)
}
switch self.variant {
case .deviceMetrics?: try {
guard case .deviceMetrics(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
}()
case .environmentMetrics?: try {
guard case .environmentMetrics(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 3)
}()
case .airQualityMetrics?: try {
guard case .airQualityMetrics(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 4)
}()
case .powerMetrics?: try {
guard case .powerMetrics(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 5)
}()
case .localStats?: try {
guard case .localStats(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 6)
}()
case .healthMetrics?: try {
guard case .healthMetrics(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 7)
}()
case .hostMetrics?: try {
guard case .hostMetrics(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 8)
}()
case nil: break
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
if _storage._time != 0 {
try visitor.visitSingularFixed32Field(value: _storage._time, fieldNumber: 1)
}
switch _storage._variant {
case .deviceMetrics?: try {
guard case .deviceMetrics(let v)? = _storage._variant else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
}()
case .environmentMetrics?: try {
guard case .environmentMetrics(let v)? = _storage._variant else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 3)
}()
case .airQualityMetrics?: try {
guard case .airQualityMetrics(let v)? = _storage._variant else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 4)
}()
case .powerMetrics?: try {
guard case .powerMetrics(let v)? = _storage._variant else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 5)
}()
case .localStats?: try {
guard case .localStats(let v)? = _storage._variant else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 6)
}()
case .healthMetrics?: try {
guard case .healthMetrics(let v)? = _storage._variant else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 7)
}()
case .hostMetrics?: try {
guard case .hostMetrics(let v)? = _storage._variant else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 8)
}()
case nil: break
}
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Telemetry, rhs: Telemetry) -> Bool {
if lhs.time != rhs.time {return false}
if lhs.variant != rhs.variant {return false}
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._time != rhs_storage._time {return false}
if _storage._variant != rhs_storage._variant {return false}
return true
}
if !storagesAreEqual {return false}
}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}