Merge pull request #1140 from meshtastic/2.5.21

2.5.21 Changes
This commit is contained in:
Garth Vander Houwen 2025-03-30 12:11:04 -07:00 committed by GitHub
commit 04579d4351
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
77 changed files with 4775 additions and 2663 deletions

View file

@ -4,29 +4,31 @@ Thank you for considering contributing to Meshtastic! We appreciate your time an
## Table of Contents
1. [Getting Started](#getting-started)
2. [Development Workflow](#development-workflow)
- [Targeting `main`](#targeting-main)
- [Small, Incremental Changes](#small-incremental-changes)
- [Rebase Commits](#rebase-commits)
3. [Creating a Branch](#creating-a-branch)
4. [Making Changes](#making-changes)
5. [Commit Messages](#commit-messages)
6. [Merging Changes](#merging-changes)
7. [Testing](#testing)
8. [Code Review](#code-review)
9. [Documentation](#documentation)
10. [Style Guides](#style-guides)
- [Contributing to Meshtastic](#contributing-to-meshtastic)
- [Table of Contents](#table-of-contents)
- [Getting Started](#getting-started)
- [Development Workflow](#development-workflow)
- [Targeting `main`](#targeting-main)
- [Small, Incremental Changes](#small-incremental-changes)
- [Rebase Commits](#rebase-commits)
- [Creating a Branch](#creating-a-branch)
- [Making Changes](#making-changes)
- [Commit Messages](#commit-messages)
- [Merging Changes](#merging-changes)
- [Testing](#testing)
- [Code Review](#code-review)
- [Documentation](#documentation)
- [Style Guides](#style-guides)
- [Git Commit Messages](#git-commit-messages)
- [Code Style](#code-style)
11. [Community](#community)
- [Community](#community)
## Getting Started
1. Fork the repository on GitLab.
1. Fork the repository on GitHub.
2. Clone your fork to your local machine:
```sh
git clone https://gitlab.com/<your-username>/Meshtastic-Apple.git
git clone https://github.com/<your-username>/Meshtastic-Apple.git
```
3. Navigate to the project directory:
```sh

View file

@ -2203,22 +2203,6 @@
}
}
},
"Backup Database" : {
"localizations" : {
"sr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Резервна база података"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "备份数据库"
}
}
}
},
"Bandwidth" : {
"localizations" : {
"de" : {
@ -5628,6 +5612,9 @@
}
}
}
},
"Confirm" : {
},
"Connect to a Node" : {
"localizations" : {
@ -6363,6 +6350,9 @@
}
}
}
},
"Currently showing modules that may not be supported by this node." : {
},
"Currently the recommended way to update ESP32 devices is using the web flasher on a desktop computer from a chrome based browser. It does not work on mobile devices or over BLE." : {
"localizations" : {
@ -9297,6 +9287,9 @@
}
}
}
},
"Enable broadcasting packets via UDP over the local network." : {
},
"Enable Notifications" : {
"localizations" : {
@ -12138,23 +12131,6 @@
}
}
},
"HUMIDITY" : {
"extractionState" : "stale",
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "LUFTFEUCHTIGKEIT"
}
},
"sr" : {
"stringUnit" : {
"state" : "translated",
"value" : "ВЛАЖНОСТ"
}
}
}
},
"hybrid" : {
"extractionState" : "migrated",
"localizations" : {
@ -17140,71 +17116,6 @@
}
}
},
"mesh.log" : {
"extractionState" : "stale",
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Mesh Log"
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Mesh Log"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Journal du maillage"
}
},
"he" : {
"stringUnit" : {
"state" : "translated",
"value" : "יומן מש"
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
"value" : "Dziennik Sieci"
}
},
"pt-PT" : {
"stringUnit" : {
"state" : "translated",
"value" : "Log do Mesh"
}
},
"se" : {
"stringUnit" : {
"state" : "translated",
"value" : "Mesh-logg"
}
},
"sr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Логови мреже"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "Mesh 日志"
}
},
"zh-Hant-TW" : {
"stringUnit" : {
"state" : "translated",
"value" : "Mesh 紀錄檔"
}
}
}
},
"mesh.log.ambientlighting.config %@" : {
"extractionState" : "migrated",
"localizations" : {
@ -22214,21 +22125,8 @@
}
}
},
"OTA Updates are not supported on the this NRF Device." : {
"localizations" : {
"sr" : {
"stringUnit" : {
"state" : "translated",
"value" : "ОТА ажурирања нису подржана на овом NRF уређају."
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "OTA 更新不支持 NRF 设备"
}
}
}
"OTA Updates are not supported on this NRF Device." : {
},
"OTA Updates are not supported on your platform." : {
"localizations" : {
@ -23607,6 +23505,9 @@
}
}
}
},
"Radiation" : {
},
"Radio Disconnected" : {
"extractionState" : "manual",
@ -24694,22 +24595,6 @@
}
}
},
"restore" : {
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Wiederherstellen"
}
},
"sr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Обнова"
}
}
}
},
"resume" : {
"localizations" : {
"de" : {
@ -28513,6 +28398,12 @@
}
}
}
},
"Soil Moisture" : {
},
"Soil Temp" : {
},
"Specifies how long the monitored GPIO should output." : {
"localizations" : {
@ -30180,6 +30071,9 @@
}
}
}
},
"The Router roles are designed for high vantage locations like mountaintops and towers. This node needs to be able to have a good direct connection to most of the nodes on the network or else this will significantly hurt the network." : {
},
"The secondary public key authorized to send admin messages to this node." : {
"localizations" : {
@ -30353,6 +30247,9 @@
}
}
}
},
"This node does not support any configurable modules." : {
},
"This will disable fixed position and remove the currently set position." : {
"localizations" : {
@ -31503,6 +31400,9 @@
}
}
}
},
"UDP Broadcast" : {
},
"Ukraine 433mhz" : {
"extractionState" : "manual",
@ -32676,6 +32576,9 @@
}
}
}
},
"Weight" : {
},
"What does the lock mean?" : {
"localizations" : {

View file

@ -11,13 +11,23 @@
231B3F222D087A4C0069A07D /* MetricsColumnList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 231B3F1F2D087A4C0069A07D /* MetricsColumnList.swift */; };
231B3F252D087C3C0069A07D /* EnvironmentDefaultColumns.swift in Sources */ = {isa = PBXBuildFile; fileRef = 231B3F242D087C3C0069A07D /* EnvironmentDefaultColumns.swift */; };
231B3F272D0885240069A07D /* MetricsColumnDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 231B3F262D0885240069A07D /* MetricsColumnDetail.swift */; };
233E99B62D849C3D00CC3A77 /* WeatherConditionsCompactWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233E99B52D849C3D00CC3A77 /* WeatherConditionsCompactWidget.swift */; };
233E99B82D849C6500CC3A77 /* HumidityCompactWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233E99B72D849C6500CC3A77 /* HumidityCompactWidget.swift */; };
233E99BA2D849C7000CC3A77 /* PressureCompactWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233E99B92D849C7000CC3A77 /* PressureCompactWidget.swift */; };
233E99BC2D849C8C00CC3A77 /* WindCompactWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233E99BB2D849C8C00CC3A77 /* WindCompactWidget.swift */; };
233E99BE2D849D3200CC3A77 /* RadiationCompactWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233E99BD2D849D3200CC3A77 /* RadiationCompactWidget.swift */; };
233E99C12D849D6000CC3A77 /* DistanceCompactWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233E99C02D849D6000CC3A77 /* DistanceCompactWidget.swift */; };
233E99C32D849D7A00CC3A77 /* WeightCompactWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233E99C22D849D7A00CC3A77 /* WeightCompactWidget.swift */; };
233E99C52D84A0B600CC3A77 /* CompactWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233E99C42D84A0B600CC3A77 /* CompactWidget.swift */; };
233E99C72D84A70900CC3A77 /* SoilCompactWidgets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233E99C62D84A70900CC3A77 /* SoilCompactWidgets.swift */; };
233E99CB2D85AAA900CC3A77 /* RainfallCompactWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 233E99CA2D85AAA900CC3A77 /* RainfallCompactWidget.swift */; };
2344A2AB2D66974300170A77 /* ManagedAttributePropertyWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2344A2AA2D66973D00170A77 /* ManagedAttributePropertyWrapper.swift */; };
2344A2AF2D6697A700170A77 /* TelemetryEntity+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2344A2AD2D6697A700170A77 /* TelemetryEntity+CoreDataClass.swift */; };
2344A2B02D6697A700170A77 /* TelemetryEntity+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2344A2AE2D6697A700170A77 /* TelemetryEntity+CoreDataProperties.swift */; };
2344A2B12D68DFF800170A77 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25C49D8F2C471AEA0024FBD1 /* Constants.swift */; };
2373AE132D0A216C0086C749 /* MetricsChartSeries.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2373AE122D0A216C0086C749 /* MetricsChartSeries.swift */; };
2373AE152D0A24930086C749 /* MetricsSeriesList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2373AE142D0A24930086C749 /* MetricsSeriesList.swift */; };
2373AE172D0A26620086C749 /* EnviornmentDefaultSeries.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2373AE162D0A26620086C749 /* EnviornmentDefaultSeries.swift */; };
2373AE172D0A26620086C749 /* EnvironmentDefaultSeries.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2373AE162D0A26620086C749 /* EnvironmentDefaultSeries.swift */; };
251926852C3BA97800249DF5 /* FavoriteNodeButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 251926842C3BA97800249DF5 /* FavoriteNodeButton.swift */; };
251926872C3BAE2200249DF5 /* NodeAlertsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 251926862C3BAE2200249DF5 /* NodeAlertsButton.swift */; };
2519268A2C3BB1B200249DF5 /* ExchangePositionsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 251926892C3BB1B200249DF5 /* ExchangePositionsButton.swift */; };
@ -56,7 +66,6 @@
BCE2D3C52C7AE369008E6199 /* RestartNodeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE2D3C42C7AE369008E6199 /* RestartNodeIntent.swift */; };
BCE2D3C72C7B0D0A008E6199 /* ShortcutsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE2D3C62C7B0D0A008E6199 /* ShortcutsProvider.swift */; };
BCE2D3C92C7C377F008E6199 /* FactoryResetNodeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE2D3C82C7C377F008E6199 /* FactoryResetNodeIntent.swift */; };
C9697FA527933B8C00250207 /* SQLite in Frameworks */ = {isa = PBXBuildFile; productRef = C9697FA427933B8C00250207 /* SQLite */; };
D93068D32B8129510066FBC8 /* MessageContextMenuItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93068D22B8129510066FBC8 /* MessageContextMenuItems.swift */; };
D93068D52B812B700066FBC8 /* MessageDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93068D42B812B700066FBC8 /* MessageDestination.swift */; };
D93068D72B8146690066FBC8 /* MessageText.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93068D62B8146690066FBC8 /* MessageText.swift */; };
@ -277,12 +286,23 @@
231B3F202D087A4C0069A07D /* MetricTableColumn.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetricTableColumn.swift; sourceTree = "<group>"; };
231B3F242D087C3C0069A07D /* EnvironmentDefaultColumns.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentDefaultColumns.swift; sourceTree = "<group>"; };
231B3F262D0885240069A07D /* MetricsColumnDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetricsColumnDetail.swift; sourceTree = "<group>"; };
233E99B32D84969500CC3A77 /* MeshtasticDataModelV 50.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 50.xcdatamodel"; sourceTree = "<group>"; };
233E99B52D849C3D00CC3A77 /* WeatherConditionsCompactWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherConditionsCompactWidget.swift; sourceTree = "<group>"; };
233E99B72D849C6500CC3A77 /* HumidityCompactWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HumidityCompactWidget.swift; sourceTree = "<group>"; };
233E99B92D849C7000CC3A77 /* PressureCompactWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PressureCompactWidget.swift; sourceTree = "<group>"; };
233E99BB2D849C8C00CC3A77 /* WindCompactWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindCompactWidget.swift; sourceTree = "<group>"; };
233E99BD2D849D3200CC3A77 /* RadiationCompactWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadiationCompactWidget.swift; sourceTree = "<group>"; };
233E99C02D849D6000CC3A77 /* DistanceCompactWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DistanceCompactWidget.swift; sourceTree = "<group>"; };
233E99C22D849D7A00CC3A77 /* WeightCompactWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeightCompactWidget.swift; sourceTree = "<group>"; };
233E99C42D84A0B600CC3A77 /* CompactWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompactWidget.swift; sourceTree = "<group>"; };
233E99C62D84A70900CC3A77 /* SoilCompactWidgets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoilCompactWidgets.swift; sourceTree = "<group>"; };
233E99CA2D85AAA900CC3A77 /* RainfallCompactWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RainfallCompactWidget.swift; sourceTree = "<group>"; };
2344A2AA2D66973D00170A77 /* ManagedAttributePropertyWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagedAttributePropertyWrapper.swift; sourceTree = "<group>"; };
2344A2AD2D6697A700170A77 /* TelemetryEntity+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TelemetryEntity+CoreDataClass.swift"; sourceTree = "<group>"; };
2344A2AE2D6697A700170A77 /* TelemetryEntity+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TelemetryEntity+CoreDataProperties.swift"; sourceTree = "<group>"; };
2373AE122D0A216C0086C749 /* MetricsChartSeries.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetricsChartSeries.swift; sourceTree = "<group>"; };
2373AE142D0A24930086C749 /* MetricsSeriesList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetricsSeriesList.swift; sourceTree = "<group>"; };
2373AE162D0A26620086C749 /* EnviornmentDefaultSeries.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnviornmentDefaultSeries.swift; sourceTree = "<group>"; };
2373AE162D0A26620086C749 /* EnvironmentDefaultSeries.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentDefaultSeries.swift; sourceTree = "<group>"; };
251926842C3BA97800249DF5 /* FavoriteNodeButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteNodeButton.swift; sourceTree = "<group>"; };
251926862C3BAE2200249DF5 /* NodeAlertsButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeAlertsButton.swift; sourceTree = "<group>"; };
251926892C3BB1B200249DF5 /* ExchangePositionsButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExchangePositionsButton.swift; sourceTree = "<group>"; };
@ -567,7 +587,6 @@
buildActionMask = 2147483647;
files = (
25A978BA2C13F8ED0003AAE7 /* MeshtasticProtobufs in Frameworks */,
C9697FA527933B8C00250207 /* SQLite in Frameworks */,
DD0D3D222A55CEB10066DB71 /* CocoaMQTT in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -600,12 +619,30 @@
isa = PBXGroup;
children = (
231B3F242D087C3C0069A07D /* EnvironmentDefaultColumns.swift */,
2373AE162D0A26620086C749 /* EnviornmentDefaultSeries.swift */,
2373AE162D0A26620086C749 /* EnvironmentDefaultSeries.swift */,
231B3F262D0885240069A07D /* MetricsColumnDetail.swift */,
);
path = "Metrics Columns";
sourceTree = "<group>";
};
233E99B42D849C2D00CC3A77 /* Compact Widgets */ = {
isa = PBXGroup;
children = (
233E99C42D84A0B600CC3A77 /* CompactWidget.swift */,
233E99B72D849C6500CC3A77 /* HumidityCompactWidget.swift */,
233E99B52D849C3D00CC3A77 /* WeatherConditionsCompactWidget.swift */,
233E99B92D849C7000CC3A77 /* PressureCompactWidget.swift */,
233E99BB2D849C8C00CC3A77 /* WindCompactWidget.swift */,
DDFEB3BA29900C1200EE7472 /* CurrentConditionsCompact.swift */,
233E99BD2D849D3200CC3A77 /* RadiationCompactWidget.swift */,
233E99C02D849D6000CC3A77 /* DistanceCompactWidget.swift */,
233E99C22D849D7A00CC3A77 /* WeightCompactWidget.swift */,
233E99C62D84A70900CC3A77 /* SoilCompactWidgets.swift */,
233E99CA2D85AAA900CC3A77 /* RainfallCompactWidget.swift */,
);
path = "Compact Widgets";
sourceTree = "<group>";
};
2344A2AC2D66978000170A77 /* CoreData */ = {
isa = PBXGroup;
children = (
@ -777,7 +814,6 @@
isa = PBXGroup;
children = (
DD5E523E298F5A9E00D21B61 /* AirQualityIndex.swift */,
DDFEB3BA29900C1200EE7472 /* CurrentConditionsCompact.swift */,
DDA9515D2BC6F56F00CEA535 /* IndoorAirQuality.swift */,
DD354FD82BD96A0B0061A25F /* IAQScale.swift */,
DD41A61429AB0035003C5A37 /* NodeWeatherForecast.swift */,
@ -1038,6 +1074,7 @@
DDC2E18D26CE25CB0042C5E4 /* Helpers */ = {
isa = PBXGroup;
children = (
233E99B42D849C2D00CC3A77 /* Compact Widgets */,
DD6F65772C6EAB860053C113 /* Help */,
DD3CC6BD28E4CD9800FA9159 /* BatteryGauge.swift */,
DD3CC24B2C498D6C001BD3A2 /* BatteryCompact.swift */,
@ -1213,7 +1250,6 @@
);
name = Meshtastic;
packageProductDependencies = (
C9697FA427933B8C00250207 /* SQLite */,
DD0D3D212A55CEB10066DB71 /* CocoaMQTT */,
25A978B92C13F8ED0003AAE7 /* MeshtasticProtobufs */,
);
@ -1283,7 +1319,6 @@
);
mainGroup = DDC2E14B26CE248E0042C5E4;
packageReferences = (
C9697FA327933B8C00250207 /* XCRemoteSwiftPackageReference "SQLite.swift" */,
DD0D3D202A55CEB10066DB71 /* XCRemoteSwiftPackageReference "CocoaMQTT" */,
25A978B82C13F8ED0003AAE7 /* XCLocalSwiftPackageReference "MeshtasticProtobufs" */,
259792242C2F10B600AD1659 /* XCRemoteSwiftPackageReference "swift-protobuf" */,
@ -1405,8 +1440,10 @@
DDB6ABD628AE742000384BA1 /* BluetoothConfig.swift in Sources */,
251926902C3CB44900249DF5 /* ClientHistoryButton.swift in Sources */,
DDD5BB102C285FB3007E03CA /* AppLogFilter.swift in Sources */,
2373AE172D0A26620086C749 /* EnviornmentDefaultSeries.swift in Sources */,
2373AE172D0A26620086C749 /* EnvironmentDefaultSeries.swift in Sources */,
233E99B82D849C6500CC3A77 /* HumidityCompactWidget.swift in Sources */,
DD4640202AFF10F4002A5ECB /* WaypointForm.swift in Sources */,
233E99C12D849D6000CC3A77 /* DistanceCompactWidget.swift in Sources */,
DD769E0328D18BF1001A3F05 /* DeviceMetricsLog.swift in Sources */,
DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */,
DD15E4F32B8BA56E00654F61 /* PaxCounterConfig.swift in Sources */,
@ -1436,6 +1473,7 @@
DDE9659C2B1C3B6A00531070 /* RouteRecorder.swift in Sources */,
B399E8A42B6F486400E4488E /* RetryButton.swift in Sources */,
DDB8F4102A9EE5B400230ECE /* Messages.swift in Sources */,
233E99C32D849D7A00CC3A77 /* WeightCompactWidget.swift in Sources */,
DDDB26482AACD6D1003AFCB7 /* NodeMapMapkit.swift in Sources */,
DD4A911E2708C65400501B7E /* AppSettings.swift in Sources */,
DD1BD0F32C63C65E008C0C70 /* SecurityConfig.swift in Sources */,
@ -1453,6 +1491,7 @@
DD6193772862F90F00E59241 /* CannedMessagesConfig.swift in Sources */,
DD3619152B1EF9F900C41C8C /* LocationsHandler.swift in Sources */,
DD6F65792C6EADE60053C113 /* DirectMessagesHelp.swift in Sources */,
233E99B62D849C3D00CC3A77 /* WeatherConditionsCompactWidget.swift in Sources */,
25F5D5C02C3F6DA6008036E3 /* Router.swift in Sources */,
DDDB444A29F8AA3A00EE2349 /* CLLocationCoordinate2D.swift in Sources */,
25C49D902C471AEA0024FBD1 /* Constants.swift in Sources */,
@ -1488,9 +1527,11 @@
DD1BD0EE2C603C91008C0C70 /* CustomFormatters.swift in Sources */,
DD0BE3102CB9FDC4000BA445 /* DetectionSensorEnums.swift in Sources */,
DDD9E4E4284B208E003777C5 /* UserEntityExtension.swift in Sources */,
233E99CB2D85AAA900CC3A77 /* RainfallCompactWidget.swift in Sources */,
DD2553592855B52700E55709 /* PositionConfig.swift in Sources */,
DD97E96828EFE9A00056DDA4 /* About.swift in Sources */,
DDDB444029F79AB000EE2349 /* UserDefaults.swift in Sources */,
233E99BA2D849C7000CC3A77 /* PressureCompactWidget.swift in Sources */,
DDB6ABE028B13AC700384BA1 /* DeviceEnums.swift in Sources */,
DD86D40C287F401000BAEB7A /* SaveChannelQRCode.swift in Sources */,
D93068DD2B81CA820066FBC8 /* ConfigHeader.swift in Sources */,
@ -1500,6 +1541,7 @@
DDD5BB092C285DDC007E03CA /* AppLog.swift in Sources */,
BC5EBA3C2D002A2000C442FF /* MessageNodeIntent.swift in Sources */,
DD8ED9C8289CE4B900B3B0AB /* RoutingError.swift in Sources */,
233E99C52D84A0B600CC3A77 /* CompactWidget.swift in Sources */,
DDC1B81A2AB5377B00C71E39 /* MessagesTips.swift in Sources */,
DD964FC62975DBFD007C176F /* QueryCoreData.swift in Sources */,
DDB75A112A059258006ED576 /* Url.swift in Sources */,
@ -1535,6 +1577,7 @@
8D3F8A3F2D44BB02009EAAA4 /* PowerMetrics.swift in Sources */,
2519268A2C3BB1B200249DF5 /* ExchangePositionsButton.swift in Sources */,
DD86D40A287F04F100BAEB7A /* InvalidVersion.swift in Sources */,
233E99BE2D849D3200CC3A77 /* RadiationCompactWidget.swift in Sources */,
DDD94A502845C8F5004A87A0 /* DateTimeText.swift in Sources */,
DDB6ABE228B13FB500384BA1 /* PositionConfigEnums.swift in Sources */,
DD994B69295F88B60013760A /* IntervalEnums.swift in Sources */,
@ -1566,12 +1609,14 @@
DDDB26442AAC0206003AFCB7 /* NodeDetail.swift in Sources */,
DD77093F2AA1B146007A8BF0 /* UIColor.swift in Sources */,
DDF6B2482A9AEBF500BA6931 /* StoreForwardConfig.swift in Sources */,
233E99C72D84A70900CC3A77 /* SoilCompactWidgets.swift in Sources */,
BCE2D3C92C7C377F008E6199 /* FactoryResetNodeIntent.swift in Sources */,
DD93800B2BA3F968008BEC06 /* NodeMapContent.swift in Sources */,
DD41582A28585C32009B0E59 /* RangeTestConfig.swift in Sources */,
DD1925B728CDA5A400720036 /* CannedMessagesConfigEnums.swift in Sources */,
DDDB444429F8A8DD00EE2349 /* Float.swift in Sources */,
DDAB580F2B0DAFBC00147258 /* LocationEntityExtension.swift in Sources */,
233E99BC2D849C8C00CC3A77 /* WindCompactWidget.swift in Sources */,
B3E905B12B71F7F300654D07 /* TextMessageField.swift in Sources */,
BC6B45FF2CB2F98900723CEB /* SaveChannelSettingsIntent.swift in Sources */,
D93068D72B8146690066FBC8 /* MessageText.swift in Sources */,
@ -1800,7 +1845,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.5.20;
MARKETING_VERSION = 2.5.21;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTS_MACCATALYST = YES;
@ -1834,7 +1879,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.5.20;
MARKETING_VERSION = 2.5.21;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTS_MACCATALYST = YES;
@ -1866,7 +1911,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.5.20;
MARKETING_VERSION = 2.5.21;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -1899,7 +1944,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.5.20;
MARKETING_VERSION = 2.5.21;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -1968,14 +2013,6 @@
minimumVersion = 1.26.0;
};
};
C9697FA327933B8C00250207 /* XCRemoteSwiftPackageReference "SQLite.swift" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/stephencelis/SQLite.swift.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.9.2;
};
};
DD0D3D202A55CEB10066DB71 /* XCRemoteSwiftPackageReference "CocoaMQTT" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/emqx/CocoaMQTT";
@ -1995,11 +2032,6 @@
isa = XCSwiftPackageProductDependency;
productName = MeshtasticProtobufs;
};
C9697FA427933B8C00250207 /* SQLite */ = {
isa = XCSwiftPackageProductDependency;
package = C9697FA327933B8C00250207 /* XCRemoteSwiftPackageReference "SQLite.swift" */;
productName = SQLite;
};
DD0D3D212A55CEB10066DB71 /* CocoaMQTT */ = {
isa = XCSwiftPackageProductDependency;
package = DD0D3D202A55CEB10066DB71 /* XCRemoteSwiftPackageReference "CocoaMQTT" */;
@ -2011,6 +2043,7 @@
DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
233E99B32D84969500CC3A77 /* MeshtasticDataModelV 50.xcdatamodel */,
8D3F8A3D2D44B137009EAAA4 /* MeshtasticDataModelV 49.xcdatamodel */,
DDA28B1B2D32C89200EF726F /* MeshtasticDataModelV 48.xcdatamodel */,
DDDFE7402D0D4A070044463C /* MeshtasticDataModelV 47.xcdatamodel */,
@ -2061,7 +2094,7 @@
DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */,
DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */,
);
currentVersion = 8D3F8A3D2D44B137009EAAA4 /* MeshtasticDataModelV 49.xcdatamodel */;
currentVersion = 233E99B32D84969500CC3A77 /* MeshtasticDataModelV 50.xcdatamodel */;
name = Meshtastic.xcdatamodeld;
path = Meshtastic/Meshtastic.xcdatamodeld;
sourceTree = "<group>";

View file

@ -1,51 +0,0 @@
{
"originHash" : "2d0b85469585b0d6079eac292d63864096062c24848a49380b9d9727f0ceb96c",
"pins" : [
{
"identity" : "cocoamqtt",
"kind" : "remoteSourceControl",
"location" : "https://github.com/emqx/CocoaMQTT",
"state" : {
"revision" : "85387a2478551ad84f39be8a3c8587d34dd2bcf5",
"version" : "2.1.5"
}
},
{
"identity" : "mqttcocoaasyncsocket",
"kind" : "remoteSourceControl",
"location" : "https://github.com/leeway1208/MqttCocoaAsyncSocket",
"state" : {
"revision" : "ce3e18607fd01079495f86ff6195d8a3ca469f73",
"version" : "1.0.8"
}
},
{
"identity" : "sqlite.swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/stephencelis/SQLite.swift.git",
"state" : {
"revision" : "7a2e3cd27de56f6d396e84f63beefd0267b55ccb",
"version" : "0.14.1"
}
},
{
"identity" : "starscream",
"kind" : "remoteSourceControl",
"location" : "https://github.com/daltoniam/Starscream.git",
"state" : {
"revision" : "a063fda2b8145a231953c20e7a646be254365396",
"version" : "3.1.2"
}
},
{
"identity" : "swift-protobuf",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-protobuf.git",
"state" : {
"revision" : "9f0c76544701845ad98716f3f6a774a892152bcb",
"version" : "1.26.0"
}
}
],
"version" : 3
}

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>

View file

@ -1,5 +1,5 @@
{
"originHash" : "1571e0d09fede5d57a2c415019f30868d90fde5a53a863cc277593881c2dc4a5",
"originHash" : "a3033aea781828906c453276e3723177901ce64df5757de7ada28c854c9662eb",
"pins" : [
{
"identity" : "cocoamqtt",
@ -19,15 +19,6 @@
"version" : "1.0.8"
}
},
{
"identity" : "sqlite.swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/stephencelis/SQLite.swift.git",
"state" : {
"revision" : "a95fc6df17d108bd99210db5e8a9bac90fe984b8",
"version" : "0.15.3"
}
},
{
"identity" : "starscream",
"kind" : "remoteSourceControl",
@ -42,8 +33,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-protobuf.git",
"state" : {
"revision" : "edb6ed4919f7756157fe02f2552b7e3850a538e5",
"version" : "1.28.1"
"revision" : "d72aed98f8253ec1aa9ea1141e28150f408cf17f",
"version" : "1.29.0"
}
}
],

View file

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

View file

@ -0,0 +1,366 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--Generator: Apple Native CoreSVG 341-->
<svg
version="1.1"
viewBox="0 0 3300 2200"
id="svg681"
sodipodi:docname="groundmoisture.variable.svg"
inkscape:version="1.2.1 (9c6d41e, 2022-07-14)"
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">
<defs
id="defs685" />
<sodipodi:namedview
id="namedview683"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="0.55606061"
inkscape:cx="1991.6894"
inkscape:cy="935.14986"
inkscape:window-width="2560"
inkscape:window-height="1440"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="Symbols"
showguides="false" />
<g
id="Notes">
<rect
height="2200"
id="artboard"
style="fill:white;opacity:1"
width="3300"
x="0"
y="0" />
<line
style="fill:none;stroke:black;opacity:1;stroke-width:0.5;"
x1="263"
x2="3036"
y1="292"
y2="292"
id="line562" />
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;"
transform="matrix(1 0 0 1 263 322)"
id="text564">Weight/Scale Variations</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
transform="matrix(1 0 0 1 559.711 322)"
id="text566">Ultralight</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
transform="matrix(1 0 0 1 856.422 322)"
id="text568">Thin</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
transform="matrix(1 0 0 1 1153.13 322)"
id="text570">Light</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
transform="matrix(1 0 0 1 1449.84 322)"
id="text572">Regular</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
transform="matrix(1 0 0 1 1746.56 322)"
id="text574">Medium</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
transform="matrix(1 0 0 1 2043.27 322)"
id="text576">Semibold</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
transform="matrix(1 0 0 1 2339.98 322)"
id="text578">Bold</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
transform="matrix(1 0 0 1 2636.69 322)"
id="text580">Heavy</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
transform="matrix(1 0 0 1 2933.4 322)"
id="text582">Black</text>
<line
style="fill:none;stroke:black;opacity:1;stroke-width:0.5;"
x1="263"
x2="3036"
y1="1903"
y2="1903"
id="line584" />
<g
transform="matrix(0.2 0 0 0.2 263 1933)"
id="g588">
<path
d="m46.2402 4.15039c21.7773 0 39.4531-17.627 39.4531-39.4043s-17.6758-39.4043-39.4531-39.4043c-21.7285 0-39.4043 17.627-39.4043 39.4043s17.6758 39.4043 39.4043 39.4043Zm0-7.42188c-17.6758 0-31.9336-14.3066-31.9336-31.9824s14.2578-31.9824 31.9336-31.9824 31.9824 14.3066 31.9824 31.9824-14.3066 31.9824-31.9824 31.9824Zm-17.9688-31.9824c0 2.14844 1.51367 3.61328 3.75977 3.61328h10.498v10.5957c0 2.19727 1.46484 3.71094 3.61328 3.71094 2.24609 0 3.71094-1.51367 3.71094-3.71094v-10.5957h10.5957c2.19727 0 3.71094-1.46484 3.71094-3.61328 0-2.19727-1.51367-3.71094-3.71094-3.71094h-10.5957v-10.5469c0-2.24609-1.46484-3.75977-3.71094-3.75977-2.14844 0-3.61328 1.51367-3.61328 3.75977v10.5469h-10.498c-2.24609 0-3.75977 1.51367-3.75977 3.71094Z"
id="path586" />
</g>
<g
transform="matrix(0.2 0 0 0.2 281.506 1933)"
id="g592">
<path
d="m58.5449 14.5508c27.4902 0 49.8047-22.3145 49.8047-49.8047s-22.3145-49.8047-49.8047-49.8047-49.8047 22.3145-49.8047 49.8047 22.3145 49.8047 49.8047 49.8047Zm0-8.30078c-22.9492 0-41.5039-18.5547-41.5039-41.5039s18.5547-41.5039 41.5039-41.5039 41.5039 18.5547 41.5039 41.5039-18.5547 41.5039-41.5039 41.5039Zm-22.6562-41.5039c0 2.39258 1.66016 4.00391 4.15039 4.00391h14.3555v14.4043c0 2.44141 1.66016 4.15039 4.05273 4.15039 2.44141 0 4.15039-1.66016 4.15039-4.15039v-14.4043h14.4043c2.44141 0 4.15039-1.61133 4.15039-4.00391 0-2.44141-1.70898-4.15039-4.15039-4.15039h-14.4043v-14.3555c0-2.49023-1.70898-4.19922-4.15039-4.19922-2.39258 0-4.05273 1.70898-4.05273 4.19922v14.3555h-14.3555c-2.49023 0-4.15039 1.70898-4.15039 4.15039Z"
id="path590" />
</g>
<g
transform="matrix(0.2 0 0 0.2 304.924 1933)"
id="g596">
<path
d="m74.8535 28.3203c35.1074 0 63.623-28.4668 63.623-63.5742s-28.5156-63.623-63.623-63.623-63.5742 28.5156-63.5742 63.623 28.4668 63.5742 63.5742 63.5742Zm0-9.08203c-30.127 0-54.4922-24.3652-54.4922-54.4922s24.3652-54.4922 54.4922-54.4922 54.4922 24.3652 54.4922 54.4922-24.3652 54.4922-54.4922 54.4922Zm-28.8574-54.4922c0 2.58789 1.85547 4.39453 4.58984 4.39453h19.7266v19.7754c0 2.68555 1.85547 4.58984 4.44336 4.58984 2.68555 0 4.54102-1.85547 4.54102-4.58984v-19.7754h19.7754c2.68555 0 4.58984-1.80664 4.58984-4.39453 0-2.73438-1.85547-4.58984-4.58984-4.58984h-19.7754v-19.7266c0-2.73438-1.85547-4.63867-4.54102-4.63867-2.58789 0-4.44336 1.9043-4.44336 4.63867v19.7266h-19.7266c-2.73438 0-4.58984 1.85547-4.58984 4.58984Z"
id="path594" />
</g>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;"
transform="matrix(1 0 0 1 263 1953)"
id="text598">Design Variations</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 263 1971)"
id="text600">Symbols are supported in up to nine weights and three scales.</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 263 1989)"
id="text602">For optimal layout with text and other symbols, vertically align</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 263 2007)"
id="text604">symbols with the adjacent text.</text>
<line
style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;"
x1="776"
x2="776"
y1="1919"
y2="1933"
id="line606" />
<g
transform="matrix(0.2 0 0 0.2 776 1933)"
id="g610">
<path
d="m16.5527 0.78125c2.58789 0 3.85742-0.976562 4.78516-3.71094l6.29883-17.2363h28.8086l6.29883 17.2363c0.927734 2.73438 2.19727 3.71094 4.73633 3.71094 2.58789 0 4.24805-1.5625 4.24805-4.00391 0-0.830078-0.146484-1.61133-0.537109-2.63672l-22.9004-60.9863c-1.12305-2.97852-3.125-4.49219-6.25-4.49219-3.02734 0-5.07812 1.46484-6.15234 4.44336l-22.9004 61.084c-0.390625 1.02539-0.537109 1.80664-0.537109 2.63672 0 2.44141 1.5625 3.95508 4.10156 3.95508Zm13.4766-28.3691 11.8652-32.8613h0.244141l11.8652 32.8613Z"
id="path608" />
</g>
<line
style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;"
x1="792.836"
x2="792.836"
y1="1919"
y2="1933"
id="line612" />
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;"
transform="matrix(1 0 0 1 776 1953)"
id="text614">Margins</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 776 1971)"
id="text616">Leading and trailing margins on the left and right side of each symbol</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 776 1989)"
id="text618">can be adjusted by modifying the x-location of the margin guidelines.</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 776 2007)"
id="text620">Modifications are automatically applied proportionally to all</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 776 2025)"
id="text622">scales and weights.</text>
<g
transform="matrix(0.2 0 0 0.2 1289 1933)"
id="g626">
<path
d="m14.209 9.32617 8.49609 8.54492c4.29688 4.3457 9.22852 4.05273 13.8672-1.07422l53.4668-58.9355-4.83398-4.88281-53.0762 58.3984c-1.75781 2.00195-3.41797 2.49023-5.76172 0.146484l-5.85938-5.81055c-2.34375-2.29492-1.80664-4.00391 0.195312-5.81055l57.373-54.0039-4.88281-4.83398-57.959 54.4434c-4.93164 4.58984-5.32227 9.47266-1.02539 13.8184Zm32.0801-90.9668c-2.09961 2.05078-2.24609 4.93164-1.07422 6.88477 1.17188 1.80664 3.4668 2.97852 6.68945 2.14844 7.32422-1.70898 14.9414-2.00195 22.0703 2.68555l-2.92969 7.27539c-1.70898 4.15039-0.830078 7.08008 1.85547 9.81445l11.4746 11.5723c2.44141 2.44141 4.49219 2.53906 7.32422 2.05078l5.32227-0.976562 3.32031 3.36914-0.195312 2.7832c-0.195312 2.49023 0.439453 4.39453 2.88086 6.78711l3.80859 3.71094c2.39258 2.39258 5.46875 2.53906 7.8125 0.195312l14.5508-14.5996c2.34375-2.34375 2.24609-5.32227-0.146484-7.71484l-3.85742-3.80859c-2.39258-2.39258-4.24805-3.17383-6.64062-2.97852l-2.88086 0.244141-3.22266-3.17383 1.2207-5.61523c0.634766-2.83203-0.146484-5.0293-3.07617-7.95898l-10.9863-10.9375c-16.6992-16.6016-38.8672-16.2109-53.3203-1.75781Zm7.4707 1.85547c12.1582-8.88672 28.6133-7.37305 39.7461 3.75977l12.1582 12.0605c1.17188 1.17188 1.36719 2.09961 1.02539 3.80859l-1.61133 7.42188 7.51953 7.42188 4.93164-0.292969c1.26953-0.0488281 1.66016 0.0488281 2.63672 1.02539l2.88086 2.88086-12.207 12.207-2.88086-2.88086c-0.976562-0.976562-1.12305-1.36719-1.07422-2.68555l0.341797-4.88281-7.4707-7.42188-7.61719 1.26953c-1.61133 0.341797-2.34375 0.195312-3.56445-0.976562l-10.0098-10.0098c-1.26953-1.17188-1.41602-2.00195-0.634766-3.85742l4.39453-10.4492c-7.8125-7.27539-17.9688-10.4004-28.125-7.42188-0.78125 0.195312-1.07422-0.439453-0.439453-0.976562Z"
id="path624" />
</g>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;"
transform="matrix(1 0 0 1 1289 1953)"
id="text628">Exporting</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 1289 1971)"
id="text630">Symbols should be outlined when exporting to ensure the</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 1289 1989)"
id="text632">design is preserved when submitting to Xcode.</text>
<text
id="template-version"
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;"
transform="matrix(1 0 0 1 3036 1933)">Template v.6.0</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;"
transform="matrix(1 0 0 1 3036 1951)"
id="text635">Requires Xcode 16 or greater</text>
<text
id="descriptive-name"
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;"
transform="matrix(1 0 0 1 3036 1969)">Generated from thermometer.variable</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;"
transform="matrix(1 0 0 1 3036 1987)"
id="text638">Typeset at 100.0 points</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 263 726)"
id="text640">Small</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 263 1156)"
id="text642">Medium</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 263 1586)"
id="text644">Large</text>
</g>
<g
id="Guides">
<g
id="H-reference"
style="fill:#27AAE1;stroke:none;"
transform="matrix(1 0 0 1 339 696)">
<path
d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"
id="path647" />
</g>
<line
id="Baseline-S"
style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;"
x1="263"
x2="3036"
y1="696"
y2="696" />
<line
id="Capline-S"
style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;"
x1="263"
x2="3036"
y1="625.541"
y2="625.541" />
<g
id="g654"
style="fill:#27AAE1;stroke:none;"
transform="matrix(1 0 0 1 339 1126)">
<path
d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"
id="path652" />
</g>
<line
id="Baseline-M"
style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;"
x1="263"
x2="3036"
y1="1126"
y2="1126" />
<line
id="Capline-M"
style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;"
x1="263"
x2="3036"
y1="1055.54"
y2="1055.54" />
<g
id="g660"
style="fill:#27AAE1;stroke:none;"
transform="matrix(1 0 0 1 339 1556)">
<path
d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"
id="path658" />
</g>
<line
id="Baseline-L"
style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;"
x1="263"
x2="3036"
y1="1556"
y2="1556" />
<line
id="Capline-L"
style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;"
x1="263"
x2="3036"
y1="1485.54"
y2="1485.54" />
<line
id="right-margin-Black-S"
style="opacity:1;fill:none;stroke:#00aeef;stroke-width:0.5"
x1="2994.5601"
x2="2994.5601"
y1="600.78497"
y2="720.12097" />
<line
id="left-margin-Black-S"
style="opacity:1;fill:none;stroke:#00aeef;stroke-width:0.5"
x1="2882.24"
x2="2882.24"
y1="600.78497"
y2="720.12097" />
<line
id="right-margin-Regular-S"
style="opacity:1;fill:none;stroke:#00aeef;stroke-width:0.5"
x1="1492"
x2="1492"
y1="600.78497"
y2="720.12097" />
<line
id="left-margin-Regular-S"
style="opacity:1;fill:none;stroke:#00aeef;stroke-width:0.5"
x1="1395.6899"
x2="1395.6899"
y1="600.78497"
y2="720.12097" />
<line
id="right-margin-Ultralight-S"
style="opacity:1;fill:none;stroke:#00aeef;stroke-width:0.5"
x1="605.70599"
x2="605.70599"
y1="600.78497"
y2="720.12097" />
<line
id="left-margin-Ultralight-S"
style="opacity:1;fill:none;stroke:#00aeef;stroke-width:0.5"
x1="513.71698"
x2="513.71698"
y1="600.78497"
y2="720.12097" />
</g>
<g
id="Symbols">
<g
id="Black-S"
transform="matrix(1 0 0 1 2898.24 696)"
style="fill:#000000">
<path
id="path1811"
style="fill:#000000;stroke:#000000;stroke-width:0.5;stroke-dasharray:none;stroke-opacity:1"
d="m 40.160224,10.512322 c 13.8183,0 24.9508,-10.88863047 24.9508,-24.560501 0,-7.1289 -3.32,-13.6719 -5.5172,-17.7734 l -8.545,-15.869199 c -2.1972,-4.1504 -6.2011,-6.5918 -10.8886,-6.5918 -4.6387,0 -8.6426,2.4414 -10.8399,6.543 l -8.4961,15.869099 c -2.2461,4.1504 -5.6152,10.6446 -5.6152,17.8223 0,13.67187053 11.1816,24.560501 24.9512,24.560501 z m -18.6035,-24.560501 c 0,-5.5176 2.6855,-10.8399 4.8339,-14.7949 l 8.4961,-15.869199 c 1.1231,-2.0508 2.9785,-3.2226 5.2735,-3.2226 2.2949,0 4.1504,1.123 5.2734,3.2226 l 8.5449,15.869199 c 2.0996,3.955 4.7852,9.2773 4.7852,14.7949 0,10.1074205 -8.252,18.2128805 -18.6035,18.2128805 -10.3028,0 -18.6035,-8.10546 -18.6035,-18.2128805 z m 18.6035,6.5917905 c -4.9805,0 -8.9356,-3.8085895 -8.9356,-8.5448905 0,-3.2227 1.6602,-6.2989 3.7598,-10.2539 l 4.7851,-8.935599 c 0.1954,-0.3906 0.6348,-0.3906 0.8301,0 l 4.7364,8.935599 c 2.0996,3.955 3.7597,7.0312 3.7597,10.2539 0,4.736301 -3.9062,8.5448905 -8.9355,8.5448905 z M 93.819567,-0.549 c 0,2.346 -1.91,4.25 -4.25,4.25 -2.35,0 -4.25,-1.904 -4.25,-4.25 0,-0.295 -0.01,-0.589 -0.03,-0.881 -0.15,-2.341 1.62,-4.366 3.96,-4.519 2.34,-0.154 4.37,1.621 4.52,3.962 0.03,0.477 0.05,0.956 0.05,1.438 z m -5.03,-14.361 c 1.4,1.882 1.01,4.547 -0.87,5.947 -1.88,1.401 -4.55,1.011 -5.95,-0.871 -0.38,-0.515 -0.79,-1.02 -1.23,-1.516 -1.56,-1.755 -1.4,-4.443 0.36,-6 1.75,-1.556 4.44,-1.395 6,0.36 0.6,0.68 1.16,1.374 1.69,2.08 z m -11.75,-10.145 c 2.02,1.186 2.7,3.792 1.52,5.815 -1.19,2.024 -3.79,2.704 -5.82,1.518 -0.6,-0.352 -1.22,-0.695 -1.85,-1.027 -2.08,-1.088 -2.88,-3.659 -1.79,-5.737 1.09,-2.077 3.66,-2.881 5.73,-1.792 0.76,0.395 1.5,0.803 2.21,1.223 z M 5.8395672,-26.278 c 2.08,-1.089 4.6499998,-0.285 5.7399998,1.792 1.08,2.078 0.28,4.649 -1.7999998,5.737 -0.63,0.332 -1.25,0.675 -1.85,1.027 -2.02,1.186 -4.63,0.506 -5.82,-1.518 -1.17999998,-2.023 -0.5,-4.629 1.52,-5.815 0.72,-0.42 1.45,-0.828 2.21,-1.223 z m -12.26,9.288 c 1.55,-1.755 4.24,-1.916 6.00000002,-0.36 1.74999998,1.557 1.90999998,4.245 0.36,6 -0.44,0.496 -0.86,1.001 -1.24000002,1.516 -1.4,1.882 -4.06,2.272 -5.95,0.871 -1.88,-1.4 -2.27,-4.065 -0.87,-5.946 0.53,-0.707 1.09,-1.401 1.7,-2.081 z m -6.6800002,15.003 c 0.15,-2.341 2.18,-4.116 4.5200002,-3.962 2.34,0.153 4.12,2.178 3.96,4.519 -0.02,0.292 -0.03,0.586 -0.03,0.881 0,2.346 -1.9,4.25 -4.25,4.25 -2.3400002,0 -4.2500002,-1.904 -4.2500002,-4.25 0,-0.482 0.02,-0.961 0.05,-1.438 z" />
</g>
<g
id="Regular-S"
transform="matrix(1 0 0 1 1419.69 696)"
style="fill:#000000">
<path
id="path1814"
style="fill:#000000;stroke:#000000;stroke-opacity:1;stroke-width:0.5;stroke-dasharray:none"
d="m 40.829821,-13.588987 c 0,-4.9805 -2.4903,-9.8633 -4.5899,-13.7695 l -8.3984,-15.5762 c -0.9766,-1.8555 -1.8555,-2.6855 -3.711,-2.6855 -1.8554,0 -2.7343,0.83 -3.7109,2.6855 l -8.3496,15.5762 c -2.0508,3.9062 -4.5410003,8.789 -4.5410003,13.7695 0,8.9843901 7.4218003,16.2597781 16.6015003,16.2597781 9.2774,0 16.6993,-7.275388 16.6993,-16.2597781 z m -16.6993,21.9726701 c -12.3047,0 -22.3144003,-9.76563 -22.3144003,-21.9726701 0,-6.4453 3.125,-12.4023 5.2246,-16.4062 l 8.3496003,-15.6739 c 1.9043,-3.5156 4.6387,-5.664 8.7402,-5.664 4.1504,0 6.8848,2.1484 8.7891,5.664 l 8.3984,15.625 c 2.0996,4.0528 5.1758,10.0098 5.1758,16.4551 0,12.2070401 -10.0098,21.9726701 -22.3633,21.9726701 z m 46.31122,-10.4926827 c 0,1.518 -1.23,2.75 -2.75,2.75 -1.51,0 -2.75,-1.232 -2.75,-2.75 0,-0.328 -0.01,-0.654 -0.03,-0.979 -0.1,-1.515 1.05,-2.825 2.57,-2.924 1.51,-0.1 2.82,1.049 2.92,2.564 0.03,0.444 0.04,0.891 0.04,1.339 z m -4.73,-13.4640004 c 0.91,1.217 0.66,2.941 -0.56,3.848 -1.22,0.906 -2.94,0.653 -3.85,-0.564 -0.41,-0.549 -0.85,-1.088 -1.32,-1.616 -1,-1.136 -0.9,-2.875 0.24,-3.882 1.13,-1.007 2.87,-0.903 3.88,0.232 0.57,0.648 1.11,1.309 1.61,1.982 z m -11.3,-9.748 c 1.31,0.768 1.75,2.454 0.98,3.763 -0.77,1.31 -2.45,1.75 -3.76,0.982 -0.63,-0.364 -1.26,-0.718 -1.92,-1.061 -1.34,-0.704 -1.86,-2.368 -1.16,-3.712 0.7,-1.345 2.37,-1.864 3.71,-1.16 0.74,0.384 1.45,0.78 2.15,1.188 z m -58.7500004,-1.188 c 1.34,-0.704 3.01,-0.185 3.71,1.16 0.71,1.344 0.19,3.008 -1.16,3.712 -0.65,0.343 -1.29,0.697 -1.91,1.061 -1.31,0.768 -3,0.328 -3.77,-0.982 -0.76,-1.309 -0.32,-2.995 0.98,-3.763 0.7,-0.408 1.42,-0.804 2.15,-1.188 z m -11.8399996,8.954 c 1.01,-1.135 2.75,-1.239 3.89,-0.232 1.13,1.007 1.24,2.746 0.23,3.882 -0.47,0.528 -0.91,1.067 -1.32,1.616 -0.9,1.217 -2.63,1.47 -3.85,0.564 -1.21,-0.907 -1.47,-2.631 -0.56,-3.848 0.5,-0.673 1.04,-1.334 1.61,-1.982 z m -6.3,14.1070004 c 0.1,-1.515 1.41,-2.664 2.93,-2.564 1.51,0.099 2.66,1.409 2.56,2.924 -0.02,0.325 -0.03,0.651 -0.03,0.979 0,1.518 -1.23,2.75 -2.75,2.75 -1.52,0 -2.75,-1.232 -2.75,-2.75 0,-0.448 0.01,-0.895 0.04,-1.339 z m 46.60878,0.4059027 c -6.0547,0 -10.8886,-4.63867 -10.8886,-10.5468901 0,-3.6621 1.8554,-7.1777 3.9062,-11.084 l 6.6895,-12.4023 c 0.1953,-0.3418 0.4882,-0.3418 0.6836,0 l 6.5918,12.4023 c 2.0507,3.9063 4.0039,7.4219 4.0039,11.084 0,5.9082201 -4.8829,10.5468901 -10.9864,10.5468901 z" />
</g>
<g
id="Ultralight-S"
transform="matrix(1 0 0 1 531.717 696)"
style="fill:#000000">
<path
id="path1817"
style="fill:#000000;stroke:#000000;stroke-width:0.5;stroke-dasharray:none;stroke-opacity:1"
d="m 8.8343368,-13.746838 c 0,-5.673299 2.7617002,-11.221699 5.0883002,-15.497999 l 9.1216,-17.0816 c 1.0869,-1.9716 2.8223,-3.0303 4.9712,-3.0303 2.1524,0 3.8878,1.0587 4.9747,3.1212 l 9.0796,16.9873 c 2.372,4.2797 5.0849,9.8281 5.0849,15.501399 0,10.4814908 -8.5112,18.7940007 -19.1392,18.7940007 -10.6245,0 -19.1811002,-8.3125099 -19.1811002,-18.7940007 z M 28.015437,1.9680428 c 8.9595,0 16.1089,-6.9575205 16.1089,-15.7148808 0,-4.934999 -2.4448,-9.863299 -4.726,-14.087399 l -9.125,-16.9838 c -0.5679,-1.0381 -1.2652,-1.4595 -2.2579,-1.4595 -1.0381,0 -1.7353,0.4214 -2.2578,1.414 l -9.1216,17.0293 c -2.2778,4.2241 -4.7226,9.1524 -4.7226,14.087399 0,8.7573603 7.1494,15.7148808 16.102,15.7148808 z m 0,-2.12551995 c -7.7802,0 -13.9765,-6.00096055 -13.9765,-13.58936085 0,-4.433999 2.2642,-8.903299 4.4966,-13.081999 l 8.9599,-16.7163 c 0.2407,-0.478 0.8062,-0.478 1.0469,0 l 8.9531,16.7163 c 2.2324,4.1787 4.458,8.648 4.458,13.081999 0,7.5884003 -6.1543,13.58936085 -13.938,13.58936085 z M 70.343736,-2.1090003 c 0,-0.4319999 -0.014,-0.8619999 -0.042,-1.2899999 -0.073,-1.102 -1.026,-1.937 -2.127,-1.865 -1.101,0.072 -1.937,1.025 -1.865,2.127 0.023,0.341 0.034,0.684 0.034,1.0279999 0,1.104 0.896,2.0000001 2,2.0000001 1.104,0 2,-0.8960001 2,-2.0000001 z M 65.759736,-15.126 c -0.488,-0.655 -1.013,-1.3 -1.573,-1.931 -0.732,-0.826 -1.997,-0.902 -2.823,-0.169 -0.826,0.732 -0.902,1.997 -0.169,2.823 0.483,0.545 0.936,1.1 1.357,1.666 0.659,0.885 1.913,1.069 2.798,0.41 0.886,-0.659 1.07,-1.913 0.41,-2.799 z m -11.082,-9.548 c -0.686,-0.402 -1.39,-0.792 -2.113,-1.171 -0.978,-0.512 -2.187,-0.134 -2.7,0.844 -0.512,0.978 -0.134,2.187 0.844,2.7 0.666,0.348 1.315,0.708 1.946,1.078 0.953,0.558 2.179,0.238 2.737,-0.714 0.558,-0.952 0.238,-2.179 -0.714,-2.737 z M 1.6577364,-25.845 c -0.72300001,0.379 -1.42800001,0.769 -2.11300001,1.171 -0.95299999,0.558 -1.27299999,1.785 -0.71399999,2.737 0.55799999,0.952 1.78399999,1.272 2.736,0.714 0.632,-0.37 1.281,-0.73 1.947,-1.078 0.978,-0.513 1.356,-1.722 0.843,-2.7 -0.512,-0.978 -1.722,-1.356 -2.699,-0.844 z m -11.622,8.788 c -0.5600004,0.631 -1.0850004,1.276 -1.5740004,1.931 -0.659,0.886 -0.475,2.14 0.41,2.799 0.886,0.659 2.1400004,0.476 2.7990004,-0.41 0.421,-0.566 0.874,-1.121 1.357,-1.666 0.733,-0.826 0.657,-2.091 -0.169,-2.823 -0.826,-0.733 -2.091,-0.657 -2.823,0.169 z m -6.1150004,13.6579998 c -0.028,0.428 -0.042,0.858 -0.042,1.2899999 0,1.104 0.896,2.0000001 2,2.0000001 1.104,0 2,-0.8960001 2,-2.0000001 0,-0.3439999 0.011,-0.6869999 0.033,-1.0279999 0.073,-1.102 -0.763,-2.055 -1.864,-2.127 -1.102,-0.072 -2.055,0.763 -2.127,1.865 z" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 24 KiB

View file

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

View file

@ -0,0 +1,363 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--Generator: Apple Native CoreSVG 341-->
<svg
version="1.1"
viewBox="0 0 3300 2200"
id="svg681"
sodipodi:docname="groundtemp.variable.svg"
inkscape:version="1.2.1 (9c6d41e, 2022-07-14)"
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">
<defs
id="defs685" />
<sodipodi:namedview
id="namedview683"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="2.3396954"
inkscape:cx="2915.5504"
inkscape:cy="705.00629"
inkscape:window-width="1390"
inkscape:window-height="1205"
inkscape:window-x="65"
inkscape:window-y="150"
inkscape:window-maximized="0"
inkscape:current-layer="Guides"
showguides="false" />
<g
id="Notes">
<rect
height="2200"
id="artboard"
style="fill:white;opacity:1"
width="3300"
x="0"
y="0" />
<line
style="fill:none;stroke:black;opacity:1;stroke-width:0.5;"
x1="263"
x2="3036"
y1="292"
y2="292"
id="line562" />
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;"
transform="matrix(1 0 0 1 263 322)"
id="text564">Weight/Scale Variations</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
transform="matrix(1 0 0 1 559.711 322)"
id="text566">Ultralight</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
transform="matrix(1 0 0 1 856.422 322)"
id="text568">Thin</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
transform="matrix(1 0 0 1 1153.13 322)"
id="text570">Light</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
transform="matrix(1 0 0 1 1449.84 322)"
id="text572">Regular</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
transform="matrix(1 0 0 1 1746.56 322)"
id="text574">Medium</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
transform="matrix(1 0 0 1 2043.27 322)"
id="text576">Semibold</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
transform="matrix(1 0 0 1 2339.98 322)"
id="text578">Bold</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
transform="matrix(1 0 0 1 2636.69 322)"
id="text580">Heavy</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
transform="matrix(1 0 0 1 2933.4 322)"
id="text582">Black</text>
<line
style="fill:none;stroke:black;opacity:1;stroke-width:0.5;"
x1="263"
x2="3036"
y1="1903"
y2="1903"
id="line584" />
<g
transform="matrix(0.2 0 0 0.2 263 1933)"
id="g588">
<path
d="m46.2402 4.15039c21.7773 0 39.4531-17.627 39.4531-39.4043s-17.6758-39.4043-39.4531-39.4043c-21.7285 0-39.4043 17.627-39.4043 39.4043s17.6758 39.4043 39.4043 39.4043Zm0-7.42188c-17.6758 0-31.9336-14.3066-31.9336-31.9824s14.2578-31.9824 31.9336-31.9824 31.9824 14.3066 31.9824 31.9824-14.3066 31.9824-31.9824 31.9824Zm-17.9688-31.9824c0 2.14844 1.51367 3.61328 3.75977 3.61328h10.498v10.5957c0 2.19727 1.46484 3.71094 3.61328 3.71094 2.24609 0 3.71094-1.51367 3.71094-3.71094v-10.5957h10.5957c2.19727 0 3.71094-1.46484 3.71094-3.61328 0-2.19727-1.51367-3.71094-3.71094-3.71094h-10.5957v-10.5469c0-2.24609-1.46484-3.75977-3.71094-3.75977-2.14844 0-3.61328 1.51367-3.61328 3.75977v10.5469h-10.498c-2.24609 0-3.75977 1.51367-3.75977 3.71094Z"
id="path586" />
</g>
<g
transform="matrix(0.2 0 0 0.2 281.506 1933)"
id="g592">
<path
d="m58.5449 14.5508c27.4902 0 49.8047-22.3145 49.8047-49.8047s-22.3145-49.8047-49.8047-49.8047-49.8047 22.3145-49.8047 49.8047 22.3145 49.8047 49.8047 49.8047Zm0-8.30078c-22.9492 0-41.5039-18.5547-41.5039-41.5039s18.5547-41.5039 41.5039-41.5039 41.5039 18.5547 41.5039 41.5039-18.5547 41.5039-41.5039 41.5039Zm-22.6562-41.5039c0 2.39258 1.66016 4.00391 4.15039 4.00391h14.3555v14.4043c0 2.44141 1.66016 4.15039 4.05273 4.15039 2.44141 0 4.15039-1.66016 4.15039-4.15039v-14.4043h14.4043c2.44141 0 4.15039-1.61133 4.15039-4.00391 0-2.44141-1.70898-4.15039-4.15039-4.15039h-14.4043v-14.3555c0-2.49023-1.70898-4.19922-4.15039-4.19922-2.39258 0-4.05273 1.70898-4.05273 4.19922v14.3555h-14.3555c-2.49023 0-4.15039 1.70898-4.15039 4.15039Z"
id="path590" />
</g>
<g
transform="matrix(0.2 0 0 0.2 304.924 1933)"
id="g596">
<path
d="m74.8535 28.3203c35.1074 0 63.623-28.4668 63.623-63.5742s-28.5156-63.623-63.623-63.623-63.5742 28.5156-63.5742 63.623 28.4668 63.5742 63.5742 63.5742Zm0-9.08203c-30.127 0-54.4922-24.3652-54.4922-54.4922s24.3652-54.4922 54.4922-54.4922 54.4922 24.3652 54.4922 54.4922-24.3652 54.4922-54.4922 54.4922Zm-28.8574-54.4922c0 2.58789 1.85547 4.39453 4.58984 4.39453h19.7266v19.7754c0 2.68555 1.85547 4.58984 4.44336 4.58984 2.68555 0 4.54102-1.85547 4.54102-4.58984v-19.7754h19.7754c2.68555 0 4.58984-1.80664 4.58984-4.39453 0-2.73438-1.85547-4.58984-4.58984-4.58984h-19.7754v-19.7266c0-2.73438-1.85547-4.63867-4.54102-4.63867-2.58789 0-4.44336 1.9043-4.44336 4.63867v19.7266h-19.7266c-2.73438 0-4.58984 1.85547-4.58984 4.58984Z"
id="path594" />
</g>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;"
transform="matrix(1 0 0 1 263 1953)"
id="text598">Design Variations</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 263 1971)"
id="text600">Symbols are supported in up to nine weights and three scales.</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 263 1989)"
id="text602">For optimal layout with text and other symbols, vertically align</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 263 2007)"
id="text604">symbols with the adjacent text.</text>
<line
style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;"
x1="776"
x2="776"
y1="1919"
y2="1933"
id="line606" />
<g
transform="matrix(0.2 0 0 0.2 776 1933)"
id="g610">
<path
d="m16.5527 0.78125c2.58789 0 3.85742-0.976562 4.78516-3.71094l6.29883-17.2363h28.8086l6.29883 17.2363c0.927734 2.73438 2.19727 3.71094 4.73633 3.71094 2.58789 0 4.24805-1.5625 4.24805-4.00391 0-0.830078-0.146484-1.61133-0.537109-2.63672l-22.9004-60.9863c-1.12305-2.97852-3.125-4.49219-6.25-4.49219-3.02734 0-5.07812 1.46484-6.15234 4.44336l-22.9004 61.084c-0.390625 1.02539-0.537109 1.80664-0.537109 2.63672 0 2.44141 1.5625 3.95508 4.10156 3.95508Zm13.4766-28.3691 11.8652-32.8613h0.244141l11.8652 32.8613Z"
id="path608" />
</g>
<line
style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;"
x1="792.836"
x2="792.836"
y1="1919"
y2="1933"
id="line612" />
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;"
transform="matrix(1 0 0 1 776 1953)"
id="text614">Margins</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 776 1971)"
id="text616">Leading and trailing margins on the left and right side of each symbol</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 776 1989)"
id="text618">can be adjusted by modifying the x-location of the margin guidelines.</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 776 2007)"
id="text620">Modifications are automatically applied proportionally to all</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 776 2025)"
id="text622">scales and weights.</text>
<g
transform="matrix(0.2 0 0 0.2 1289 1933)"
id="g626">
<path
d="m14.209 9.32617 8.49609 8.54492c4.29688 4.3457 9.22852 4.05273 13.8672-1.07422l53.4668-58.9355-4.83398-4.88281-53.0762 58.3984c-1.75781 2.00195-3.41797 2.49023-5.76172 0.146484l-5.85938-5.81055c-2.34375-2.29492-1.80664-4.00391 0.195312-5.81055l57.373-54.0039-4.88281-4.83398-57.959 54.4434c-4.93164 4.58984-5.32227 9.47266-1.02539 13.8184Zm32.0801-90.9668c-2.09961 2.05078-2.24609 4.93164-1.07422 6.88477 1.17188 1.80664 3.4668 2.97852 6.68945 2.14844 7.32422-1.70898 14.9414-2.00195 22.0703 2.68555l-2.92969 7.27539c-1.70898 4.15039-0.830078 7.08008 1.85547 9.81445l11.4746 11.5723c2.44141 2.44141 4.49219 2.53906 7.32422 2.05078l5.32227-0.976562 3.32031 3.36914-0.195312 2.7832c-0.195312 2.49023 0.439453 4.39453 2.88086 6.78711l3.80859 3.71094c2.39258 2.39258 5.46875 2.53906 7.8125 0.195312l14.5508-14.5996c2.34375-2.34375 2.24609-5.32227-0.146484-7.71484l-3.85742-3.80859c-2.39258-2.39258-4.24805-3.17383-6.64062-2.97852l-2.88086 0.244141-3.22266-3.17383 1.2207-5.61523c0.634766-2.83203-0.146484-5.0293-3.07617-7.95898l-10.9863-10.9375c-16.6992-16.6016-38.8672-16.2109-53.3203-1.75781Zm7.4707 1.85547c12.1582-8.88672 28.6133-7.37305 39.7461 3.75977l12.1582 12.0605c1.17188 1.17188 1.36719 2.09961 1.02539 3.80859l-1.61133 7.42188 7.51953 7.42188 4.93164-0.292969c1.26953-0.0488281 1.66016 0.0488281 2.63672 1.02539l2.88086 2.88086-12.207 12.207-2.88086-2.88086c-0.976562-0.976562-1.12305-1.36719-1.07422-2.68555l0.341797-4.88281-7.4707-7.42188-7.61719 1.26953c-1.61133 0.341797-2.34375 0.195312-3.56445-0.976562l-10.0098-10.0098c-1.26953-1.17188-1.41602-2.00195-0.634766-3.85742l4.39453-10.4492c-7.8125-7.27539-17.9688-10.4004-28.125-7.42188-0.78125 0.195312-1.07422-0.439453-0.439453-0.976562Z"
id="path624" />
</g>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;"
transform="matrix(1 0 0 1 1289 1953)"
id="text628">Exporting</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 1289 1971)"
id="text630">Symbols should be outlined when exporting to ensure the</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 1289 1989)"
id="text632">design is preserved when submitting to Xcode.</text>
<text
id="template-version"
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;"
transform="matrix(1 0 0 1 3036 1933)">Template v.6.0</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;"
transform="matrix(1 0 0 1 3036 1951)"
id="text635">Requires Xcode 16 or greater</text>
<text
id="descriptive-name"
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;"
transform="matrix(1 0 0 1 3036 1969)">Generated from thermometer.variable</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;"
transform="matrix(1 0 0 1 3036 1987)"
id="text638">Typeset at 100.0 points</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 263 726)"
id="text640">Small</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 263 1156)"
id="text642">Medium</text>
<text
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
transform="matrix(1 0 0 1 263 1586)"
id="text644">Large</text>
</g>
<g
id="Guides">
<g
id="H-reference"
style="fill:#27AAE1;stroke:none;"
transform="matrix(1 0 0 1 339 696)">
<path
d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"
id="path647" />
</g>
<line
id="Baseline-S"
style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;"
x1="263"
x2="3036"
y1="696"
y2="696" />
<line
id="Capline-S"
style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;"
x1="263"
x2="3036"
y1="625.541"
y2="625.541" />
<g
id="g654"
style="fill:#27AAE1;stroke:none;"
transform="matrix(1 0 0 1 339 1126)">
<path
d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"
id="path652" />
</g>
<line
id="Baseline-M"
style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;"
x1="263"
x2="3036"
y1="1126"
y2="1126" />
<line
id="Capline-M"
style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;"
x1="263"
x2="3036"
y1="1055.54"
y2="1055.54" />
<g
id="g660"
style="fill:#27AAE1;stroke:none;"
transform="matrix(1 0 0 1 339 1556)">
<path
d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"
id="path658" />
</g>
<line
id="Baseline-L"
style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;"
x1="263"
x2="3036"
y1="1556"
y2="1556" />
<line
id="Capline-L"
style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;"
x1="263"
x2="3036"
y1="1485.54"
y2="1485.54" />
<line
id="right-margin-Black-S"
style="opacity:1;fill:none;stroke:#00aeef;stroke-width:0.5"
x1="2994.5601"
x2="2994.5601"
y1="600.78497"
y2="720.12097" />
<line
id="left-margin-Black-S"
style="opacity:1;fill:none;stroke:#00aeef;stroke-width:0.5"
x1="2882.24"
x2="2882.24"
y1="600.78497"
y2="720.12097" />
<line
id="right-margin-Regular-S"
style="opacity:1;fill:none;stroke:#00aeef;stroke-width:0.5"
x1="1492"
x2="1492"
y1="600.78497"
y2="720.12097" />
<line
id="left-margin-Regular-S"
style="opacity:1;fill:none;stroke:#00aeef;stroke-width:0.5"
x1="1395.6899"
x2="1395.6899"
y1="600.78497"
y2="720.12097" />
<line
id="right-margin-Ultralight-S"
style="opacity:1;fill:none;stroke:#00aeef;stroke-width:0.5"
x1="605.70599"
x2="605.70599"
y1="600.78497"
y2="720.12097" />
<line
id="left-margin-Ultralight-S"
style="opacity:1;fill:none;stroke:#00aeef;stroke-width:0.5"
x1="513.71698"
x2="513.71698"
y1="600.78497"
y2="720.12097" />
</g>
<g
id="Symbols">
<g
id="Black-S"
transform="matrix(1 0 0 1 2898.24 696)">
<path
d="m 93.819567,-0.549 c 0,2.346 -1.91,4.25 -4.25,4.25 -2.35,0 -4.25,-1.904 -4.25,-4.25 0,-0.295 -0.01,-0.589 -0.03,-0.881 -0.15,-2.341 1.62,-4.366 3.96,-4.519 2.34,-0.154 4.37,1.621 4.52,3.962 0.03,0.477 0.05,0.956 0.05,1.438 z m -5.03,-14.361 c 1.4,1.882 1.01,4.547 -0.87,5.947 -1.88,1.401 -4.55,1.011 -5.95,-0.871 -0.38,-0.515 -0.79,-1.02 -1.23,-1.516 -1.56,-1.755 -1.4,-4.443 0.36,-6 1.75,-1.556 4.44,-1.395 6,0.36 0.6,0.68 1.16,1.374 1.69,2.08 z m -11.75,-10.145 c 2.02,1.186 2.7,3.792 1.52,5.815 -1.19,2.024 -3.79,2.704 -5.82,1.518 -0.6,-0.352 -1.22,-0.695 -1.85,-1.027 -2.08,-1.088 -2.88,-3.659 -1.79,-5.737 1.09,-2.077 3.66,-2.881 5.73,-1.792 0.76,0.395 1.5,0.803 2.21,1.223 z M 5.8395672,-26.278 c 2.08,-1.089 4.6499998,-0.285 5.7399998,1.792 1.08,2.078 0.28,4.649 -1.7999998,5.737 -0.63,0.332 -1.25,0.675 -1.85,1.027 -2.02,1.186 -4.63,0.506 -5.82,-1.518 -1.17999998,-2.023 -0.5,-4.629 1.52,-5.815 0.72,-0.42 1.45,-0.828 2.21,-1.223 z m -12.26,9.288 c 1.55,-1.755 4.24,-1.916 6.00000002,-0.36 1.74999998,1.557 1.90999998,4.245 0.36,6 -0.44,0.496 -0.86,1.001 -1.24000002,1.516 -1.4,1.882 -4.06,2.272 -5.95,0.871 -1.88,-1.4 -2.27,-4.065 -0.87,-5.946 0.53,-0.707 1.09,-1.401 1.7,-2.081 z m -6.6800002,15.003 c 0.15,-2.341 2.18,-4.116 4.5200002,-3.962 2.34,0.153 4.12,2.178 3.96,4.519 -0.02,0.292 -0.03,0.586 -0.03,0.881 0,2.346 -1.9,4.25 -4.25,4.25 -2.3400002,0 -4.2500002,-1.904 -4.2500002,-4.25 0,-0.482 0.02,-0.961 0.05,-1.438 z m 53.49,13.315 c 13.96,0 25.34,-10.937 25.34,-24.951 0,-6.592 -2.44,-12.256 -6.99,-16.797 -0.73,-0.732 -0.83,-1.074 -0.83,-2.051 v -29.638 c 0,-11.182 -7.12,-18.702 -17.52,-18.702 -10.55,0 -17.63,7.52 -17.63,18.702 v 29.638 c 0,0.977 -0.1,1.367 -0.83,2.051 -4.69,4.346 -6.98,10.205 -6.98,16.797 0,14.014 11.37,24.951 25.44,24.951 z m 0,-11.474 c -7.72,0 -13.92,-6.25 -13.92,-13.965 0,-5.176 2.34,-8.985 6.3,-11.573 1.12,-0.732 1.56,-1.318 1.56,-2.832 v -33.3 c 0,-4.493 2.44,-7.471 6.06,-7.471 3.51,0 5.95,2.978 5.95,7.471 v 33.3 c 0,1.514 0.44,2.1 1.57,2.832 3.95,2.588 6.29,6.397 6.29,11.573 0,7.715 -6.2,13.965 -13.81,13.965 z m -0.05,-5.176 c 4.83,0 8.74,-3.907 8.74,-8.789 0,-3.369 -1.91,-6.153 -4.69,-7.666 -1.17,-0.635 -1.56,-1.075 -1.56,-2.832 v -10.254 c 0,-1.367 -1.13,-2.49 -2.49,-2.49 -1.37,0 -2.49,1.123 -2.49,2.49 v 10.254 c 0,1.757 -0.4,2.197 -1.57,2.832 -2.78,1.513 -4.68,4.297 -4.68,7.666 0,4.882 3.9,8.789 8.74,8.789 z m 0,-36.035 c 1.36,0 2.49,-1.123 2.49,-2.491 0,-1.367 -1.13,-2.49 -2.49,-2.49 -1.37,0 -2.49,1.123 -2.49,2.49 0,1.368 1.12,2.491 2.49,2.491 z m 0,-8.985 c 1.36,0 2.49,-1.123 2.49,-2.49 0,-1.367 -1.13,-2.49 -2.49,-2.49 -1.37,0 -2.49,1.123 -2.49,2.49 0,1.367 1.12,2.49 2.49,2.49 z m 0,-8.984 c 1.36,0 2.49,-1.123 2.49,-2.49 0,-1.368 -1.13,-2.491 -2.49,-2.491 -1.37,0 -2.49,1.123 -2.49,2.491 0,1.367 1.12,2.49 2.49,2.49 z"
style="clip-rule:evenodd;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.5px;stroke-linejoin:round;stroke-miterlimit:2"
id="path4426" />
</g>
<g
id="Regular-S"
transform="matrix(1 0 0 1 1419.69 696)">
<path
d="m 70.441741,-2.1089996 c 0,1.518 -1.23,2.75 -2.75,2.75 -1.51,0 -2.75,-1.232 -2.75,-2.75 0,-0.328 -0.01,-0.654 -0.03,-0.979 -0.1,-1.515 1.05,-2.825 2.57,-2.924 1.51,-0.1 2.82,1.049 2.92,2.564 0.03,0.444 0.04,0.891 0.04,1.339 z m -4.73,-13.4640004 c 0.91,1.217 0.66,2.941 -0.56,3.848 -1.22,0.906 -2.94,0.653 -3.85,-0.564 -0.41,-0.549 -0.85,-1.088 -1.32,-1.616 -1,-1.136 -0.9,-2.875 0.24,-3.882 1.13,-1.007 2.87,-0.903 3.88,0.232 0.57,0.648 1.11,1.309 1.61,1.982 z m -11.3,-9.748 c 1.31,0.768 1.75,2.454 0.98,3.763 -0.77,1.31 -2.45,1.75 -3.76,0.982 -0.63,-0.364 -1.26,-0.718 -1.92,-1.061 -1.34,-0.704 -1.86,-2.368 -1.16,-3.712 0.7,-1.345 2.37,-1.864 3.71,-1.16 0.74,0.384 1.45,0.78 2.15,1.188 z m -58.7500004,-1.188 c 1.34,-0.704 3.01,-0.185 3.71,1.16 0.71,1.344 0.19,3.008 -1.16,3.712 -0.65,0.343 -1.29,0.697 -1.91,1.061 -1.31,0.768 -3,0.328 -3.77,-0.982 -0.76,-1.309 -0.32,-2.995 0.98,-3.763 0.7,-0.408 1.42,-0.804 2.15,-1.188 z m -11.8399996,8.954 c 1.01,-1.135 2.75,-1.239 3.89,-0.232 1.13,1.007 1.24,2.746 0.23,3.882 -0.47,0.528 -0.91,1.067 -1.32,1.616 -0.9,1.217 -2.63,1.47 -3.85,0.564 -1.21,-0.907 -1.47,-2.631 -0.56,-3.848 0.5,-0.673 1.04,-1.334 1.61,-1.982 z m -6.3,14.1070004 c 0.1,-1.515 1.41,-2.664 2.93,-2.564 1.51,0.099 2.66,1.409 2.56,2.924 -0.02,0.325 -0.03,0.651 -0.03,0.979 0,1.518 -1.23,2.75 -2.75,2.75 -1.52,0 -2.75,-1.232 -2.75,-2.75 0,-0.448 0.01,-0.895 0.04,-1.339 z m 46.47,10.772 c 11.23,0 20.36,-9.131 20.36,-20.3610004 0,-5.908 -2.44,-11.084 -6.98,-15.283 -0.79,-0.733 -0.93,-1.123 -0.93,-2.149 v -33.789 c 0,-8.154 -5.03,-13.623 -12.45,-13.623 -7.48,0 -12.55,5.469 -12.55,13.623 v 33.789 c 0,1.026 -0.15,1.416 -0.88,2.149 -4.5400004,4.199 -6.9800004,9.375 -6.9800004,15.283 0,11.2300004 9.1300004,20.3610004 20.4100004,20.3610004 z m 0,-6.347 c -7.77,0 -14.0200004,-6.299 -14.0200004,-14.0140004 0,-4.736 2.3000004,-9.033 6.3000004,-11.67 1.12,-0.732 1.56,-1.367 1.56,-2.881 v -36.377 c 0,-4.541 2.49,-7.519 6.16,-7.519 3.61,0 6.05,2.978 6.05,7.519 v 36.377 c 0,1.514 0.49,2.149 1.61,2.881 3.96,2.637 6.3,6.934 6.3,11.67 0,7.7150004 -6.25,14.0140004 -13.96,14.0140004 z m -0.05,-5.176 c 4.93,0 8.88,-3.955 8.88,-8.8870004 0,-3.418 -1.95,-6.25 -4.73,-7.764 -1.22,-0.683 -1.61,-1.123 -1.61,-2.88 v -13.819 c 0,-1.416 -1.13,-2.539 -2.54,-2.539 -1.37,0 -2.49,1.123 -2.49,2.539 v 13.819 c 0,1.757 -0.39,2.197 -1.62,2.88 -2.83,1.514 -4.73,4.346 -4.73,7.764 0,4.9320004 3.95,8.8870004 8.84,8.8870004 z m 0,-39.6000004 c 1.41,0 2.54,-1.172 2.54,-2.539 0,-1.416 -1.13,-2.539 -2.54,-2.539 -1.37,0 -2.49,1.123 -2.49,2.539 0,1.367 1.12,2.539 2.49,2.539 z m 0,-8.838 c 1.41,0 2.54,-1.172 2.54,-2.539 0,-1.416 -1.13,-2.539 -2.54,-2.539 -1.37,0 -2.49,1.123 -2.49,2.539 0,1.367 1.12,2.539 2.49,2.539 z m 0,-8.789 c 1.41,0 2.54,-1.172 2.54,-2.539 0,-1.416 -1.13,-2.539 -2.54,-2.539 -1.37,0 -2.49,1.123 -2.49,2.539 0,1.367 1.12,2.539 2.49,2.539 z"
style="clip-rule:evenodd;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.5px;stroke-linejoin:round;stroke-miterlimit:2"
id="path4429" />
</g>
<g
id="Ultralight-S"
transform="matrix(1 0 0 1 531.717 696)">
<path
d="m 70.343736,-2.1090003 c 0,-0.4319999 -0.014,-0.8619999 -0.042,-1.2899999 -0.073,-1.102 -1.026,-1.937 -2.127,-1.865 -1.101,0.072 -1.937,1.025 -1.865,2.127 0.023,0.341 0.034,0.684 0.034,1.0279999 0,1.104 0.896,2.0000001 2,2.0000001 1.104,0 2,-0.8960001 2,-2.0000001 z M 65.759736,-15.126 c -0.488,-0.655 -1.013,-1.3 -1.573,-1.931 -0.732,-0.826 -1.997,-0.902 -2.823,-0.169 -0.826,0.732 -0.902,1.997 -0.169,2.823 0.483,0.545 0.936,1.1 1.357,1.666 0.659,0.885 1.913,1.069 2.798,0.41 0.886,-0.659 1.07,-1.913 0.41,-2.799 z m -11.082,-9.548 c -0.686,-0.402 -1.39,-0.792 -2.113,-1.171 -0.978,-0.512 -2.187,-0.134 -2.7,0.844 -0.512,0.978 -0.134,2.187 0.844,2.7 0.666,0.348 1.315,0.708 1.946,1.078 0.953,0.558 2.179,0.238 2.737,-0.714 0.558,-0.952 0.238,-2.179 -0.714,-2.737 z M 1.6577364,-25.845 c -0.72300001,0.379 -1.42800001,0.769 -2.11300001,1.171 -0.95299999,0.558 -1.27299999,1.785 -0.71399999,2.737 0.55799999,0.952 1.78399999,1.272 2.736,0.714 0.632,-0.37 1.281,-0.73 1.947,-1.078 0.978,-0.513 1.356,-1.722 0.843,-2.7 -0.512,-0.978 -1.722,-1.356 -2.699,-0.844 z m -11.622,8.788 c -0.5600004,0.631 -1.0850004,1.276 -1.5740004,1.931 -0.659,0.886 -0.475,2.14 0.41,2.799 0.886,0.659 2.1400004,0.476 2.7990004,-0.41 0.421,-0.566 0.874,-1.121 1.357,-1.666 0.733,-0.826 0.657,-2.091 -0.169,-2.823 -0.826,-0.733 -2.091,-0.657 -2.823,0.169 z m -6.1150004,13.6579998 c -0.028,0.428 -0.042,0.858 -0.042,1.2899999 0,1.104 0.896,2.0000001 2,2.0000001 1.104,0 2,-0.8960001 2,-2.0000001 0,-0.3439999 0.011,-0.6869999 0.033,-1.0279999 0.073,-1.102 -0.763,-2.055 -1.864,-2.127 -1.102,-0.072 -2.055,0.763 -2.127,1.865 z m 43.192,10.087 c 10.095,0 18.227,-8.1770001 18.227,-18.1809998 0,-5.409 -2.214,-10.131 -6.891,-13.876 -1.145,-0.914 -1.382,-1.577 -1.382,-3.192 v -38.103 c 0,-6.202 -4.121,-10.581 -9.954,-10.581 -5.836,0 -9.96,4.379 -9.96,10.581 v 38.103 c 0,1.615 -0.238,2.278 -1.379,3.192 -4.677,3.745 -6.8909996,8.467 -6.8909996,13.876 0,10.0039997 8.1319996,18.1809998 18.2299996,18.1809998 z m 0,-2.033 c -8.899,0 -16.148,-7.298 -16.148,-16.1479998 0,-4.873 2.023,-9.306 6.39,-12.487 1.395,-1.005 1.926,-2.23 1.926,-3.971 v -38.693 c 0,-4.95 3.262,-8.473 7.832,-8.473 4.567,0 7.826,3.523 7.826,8.473 v 38.693 c 0,1.741 0.534,2.966 1.929,3.971 4.364,3.181 6.39,7.614 6.39,12.487 0,8.8499998 -7.249,16.1479998 -16.145,16.1479998 z m -0.003,-5.13100005 c 6.112,0 10.975,-4.90799995 10.975,-11.02099975 0,-4.28 -2.362,-7.793 -5.917,-9.67 -1.447,-0.775 -1.929,-1.351 -1.929,-3.517 v -10.503 c 0,-1.734 -1.395,-3.13 -3.129,-3.13 -1.731,0 -3.126,1.396 -3.126,3.13 v 10.503 c 0,2.166 -0.482,2.742 -1.929,3.517 -3.559,1.877 -5.917,5.39 -5.917,9.67 0,6.1129998 4.863,11.02099975 10.972,11.02099975 z m 0,-42.05099975 c 1.734,0 3.129,-1.445 3.129,-3.175 0,-1.734 -1.395,-3.129 -3.129,-3.129 -1.731,0 -3.126,1.395 -3.126,3.129 0,1.73 1.395,3.175 3.126,3.175 z m 0,-10.473 c 1.734,0 3.129,-1.399 3.129,-3.129 0,-1.78 -1.395,-3.175 -3.129,-3.175 -1.731,0 -3.126,1.395 -3.126,3.175 0,1.73 1.395,3.129 3.126,3.129 z m 0,-10.515 c 1.734,0 3.129,-1.399 3.129,-3.129 0,-1.734 -1.395,-3.175 -3.129,-3.175 -1.731,0 -3.126,1.441 -3.126,3.175 0,1.73 1.395,3.129 3.126,3.129 z"
style="clip-rule:evenodd;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.5px;stroke-linejoin:round;stroke-miterlimit:2"
id="path4432" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 25 KiB

View file

@ -29,6 +29,8 @@ public struct ManagedAttribute<Value: Numeric> {
converter = { $0.int32Value as? Value }
} else if Value.self == Int64.self {
converter = { $0.int64Value as? Value }
} else if Value.self == UInt32.self {
converter = { $0.uint32Value as? Value }
} else {
fatalError("Unsupported type: \(Value.self)")
}

View file

@ -10,17 +10,17 @@ import Foundation
extension Date {
var lastHeard: String {
if timeIntervalSince1970 > 0 {
if self.timeIntervalSince1970 > 0 && self < Calendar.current.date(byAdding: .day, value: 1, to: Date())! {
formatted()
} else {
"unknown"
"unknown.age".localized
}
}
func formattedDate(format: String) -> String {
let dateformat = DateFormatter()
dateformat.dateFormat = format
if self > Calendar.current.date(byAdding: .year, value: -5, to: Date())! {
if self.timeIntervalSince1970 > 0 && self < Calendar.current.date(byAdding: .day, value: 1, to: Date())! {
return dateformat.string(from: self)
} else {
return "unknown.age".localized

View file

@ -93,12 +93,51 @@ extension String {
// Filter out variation selectors from the string
var withoutVariationSelectors: String {
return self.unicodeScalars
.filter { scalar in
return !scalar.properties.isVariationSelector
var scalars: [UnicodeScalar] = []
var previousWasASCII = false
for scalar in self.unicodeScalars {
if scalar.properties.isVariationSelector {
// Only keep variation selector if the previous character was ASCII
if previousWasASCII {
scalars.append(scalar)
}
// No need to update previousWasASCII since variation selectors aren't characters
// Shouldn't have 2 in a row
} else {
scalars.append(scalar)
previousWasASCII = scalar.isASCII
}
.compactMap { UnicodeScalar($0) }
}
return scalars.compactMap { UnicodeScalar($0) }
.map { String($0) }
.joined()
}
// Adds variation selectors to prefer the graphical form of emoji.
// Looks ahead to make sure that the variation selector is not already applied.
var addingVariationSelectors: String {
var result = ""
let scalars = self.unicodeScalars
var index = scalars.startIndex
while index < scalars.endIndex {
let currentScalar = scalars[index]
result += String(currentScalar)
if currentScalar.properties.isEmoji && !currentScalar.properties.isEmojiPresentation && !currentScalar.isASCII {
// Check if the next scalar is U+FE0F
let nextIndex = scalars.index(after: index)
if nextIndex < scalars.endIndex && scalars[nextIndex].value == 0xFE0F {
// Already has variation selector; skip the next scalar
index = nextIndex
} else {
// Append variation selector
result += String(UnicodeScalar(0xFE0F)!)
}
}
// Move to the next scalar
index = scalars.index(after: index)
}
return result
}
}

View file

@ -690,24 +690,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
connectedPeripheral.longName = myInfo?.bleName ?? "unknown".localized
let newConnection = Int64(UserDefaults.preferredPeripheralNum) != Int64(decodedInfo.myInfo.myNodeNum)
if newConnection {
let container = NSPersistentContainer(name: "Meshtastic")
if let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let databasePath = url.appendingPathComponent("backup")
.appendingPathComponent("\(UserDefaults.preferredPeripheralNum)")
.appendingPathComponent("Meshtastic.sqlite")
if FileManager.default.fileExists(atPath: databasePath.path) {
do {
disconnectPeripheral(reconnect: false)
try container.restorePersistentStore(from: databasePath)
UserDefaults.preferredPeripheralNum = Int(myInfo?.myNodeNum ?? 0)
context.refreshAllObjects()
Logger.data.notice("🗂️ Restored Core data for /\(UserDefaults.preferredPeripheralNum, privacy: .public)")
connectTo(peripheral: peripheral)
} catch {
Logger.data.error("🗂️ Restore Core data copy error: \(error, privacy: .public)")
}
}
}
// Onboard a new device connection here
}
}
tryClearExistingChannels()
@ -1006,6 +989,8 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
Logger.mesh.info("🕸️ MESH PACKET received for ATAK Plugin App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")")
case .powerstressApp:
Logger.mesh.info("🕸️ MESH PACKET received for Power Stress App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")")
case .reticulumTunnelApp:
Logger.mesh.info("🕸️ MESH PACKET received for Reticulum Tunnel App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")")
}
if decodedInfo.configCompleteID != 0 && decodedInfo.configCompleteID == configNonce {

View file

@ -226,6 +226,7 @@ func deviceMetadataPacket (metadata: DeviceMetadata, fromNum: Int64, sessionPass
newMetadata.hasEthernet = metadata.hasEthernet_p
newMetadata.role = Int32(metadata.role.rawValue)
newMetadata.positionFlags = Int32(metadata.positionFlags)
newMetadata.excludedModules = Int32(metadata.excludedModules)
// Swift does strings weird, this does work to get the version without the github hash
let lastDotIndex = metadata.firmwareVersion.lastIndex(of: ".")
var version = metadata.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset: 6, in: metadata.firmwareVersion))]
@ -723,10 +724,20 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage
telemetry.current = telemetryMessage.environmentMetrics.hasCurrent.then(telemetryMessage.environmentMetrics.current)
telemetry.voltage = telemetryMessage.environmentMetrics.hasVoltage.then(telemetryMessage.environmentMetrics.voltage)
telemetry.weight = telemetryMessage.environmentMetrics.hasWeight.then(telemetryMessage.environmentMetrics.weight)
telemetry.distance = telemetryMessage.environmentMetrics.hasDistance.then(telemetryMessage.environmentMetrics.distance)
telemetry.windSpeed = telemetryMessage.environmentMetrics.hasWindSpeed.then(telemetryMessage.environmentMetrics.windSpeed)
telemetry.windGust = telemetryMessage.environmentMetrics.hasWindGust.then(telemetryMessage.environmentMetrics.windGust)
telemetry.windLull = telemetryMessage.environmentMetrics.hasWindLull.then(telemetryMessage.environmentMetrics.windLull)
telemetry.windDirection = telemetryMessage.environmentMetrics.hasWindDirection.then(Int32(truncatingIfNeeded: telemetryMessage.environmentMetrics.windDirection))
telemetry.irLux = telemetryMessage.environmentMetrics.hasIrLux.then(telemetryMessage.environmentMetrics.irLux)
telemetry.lux = telemetryMessage.environmentMetrics.hasLux.then(telemetryMessage.environmentMetrics.lux)
telemetry.whiteLux = telemetryMessage.environmentMetrics.hasWhiteLux.then(telemetryMessage.environmentMetrics.whiteLux)
telemetry.uvLux = telemetryMessage.environmentMetrics.hasUvLux.then(telemetryMessage.environmentMetrics.uvLux)
telemetry.radiation = telemetryMessage.environmentMetrics.hasRadiation.then(telemetryMessage.environmentMetrics.radiation)
telemetry.rainfall1H = telemetryMessage.environmentMetrics.hasRainfall1H.then(telemetryMessage.environmentMetrics.rainfall1H)
telemetry.rainfall24H = telemetryMessage.environmentMetrics.hasRainfall24H.then(telemetryMessage.environmentMetrics.rainfall24H)
telemetry.soilTemperature = telemetryMessage.environmentMetrics.hasSoilTemperature.then(telemetryMessage.environmentMetrics.soilTemperature)
telemetry.soilMoisture = telemetryMessage.environmentMetrics.hasSoilMoisture.then(telemetryMessage.environmentMetrics.soilMoisture)
telemetry.metricsType = 1
} else if telemetryMessage.variant == Telemetry.OneOf_Variant.localStats(telemetryMessage.localStats) {
// Local Stats for Live activity

View file

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

View file

@ -0,0 +1,504 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="23605" systemVersion="24D81" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="AmbientLightingConfigEntity" representedClassName="AmbientLightingConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="blue" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="current" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="green" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="ledState" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="red" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="ambientLightingConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="ambientLightingConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="BluetoothConfigEntity" representedClassName="BluetoothConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="deviceLoggingEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="fixedPin" optional="YES" attributeType="Integer 32" defaultValueString="123456" usesScalarValueType="YES"/>
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="bluetoothConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="bluetoothConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="CannedMessageConfigEntity" representedClassName="CannedMessageConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="inputbrokerEventCcw" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="inputbrokerEventCw" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="inputbrokerEventPress" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="inputbrokerPinA" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="inputbrokerPinB" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
<attribute name="inputbrokerPinPress" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
<attribute name="messages" optional="YES" attributeType="String" minValueString="0" maxValueString="198"/>
<attribute name="rotary1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="sendBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="updown1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<relationship name="cannedMessagesConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="cannedMessageConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="ChannelEntity" representedClassName="ChannelEntity" syncable="YES" codeGenerationType="class">
<attribute name="downlinkEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="id" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="index" attributeType="Integer 32" minValueString="0" maxValueString="13" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="mute" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="name" optional="YES" attributeType="String"/>
<attribute name="positionPrecision" optional="YES" attributeType="Integer 32" defaultValueString="32" usesScalarValueType="YES"/>
<attribute name="psk" optional="YES" attributeType="Binary"/>
<attribute name="role" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="uplinkEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<relationship name="myInfoChannel" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="channels" inverseEntity="MyInfoEntity"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="index"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="DetectionSensorConfigEntity" representedClassName="DetectionSensorConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="minimumBroadcastSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="monitorPin" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="name" optional="YES" attributeType="String"/>
<attribute name="sendBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="stateBroadcastSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="triggerType" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
<attribute name="usePullup" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<relationship name="detectionSensorConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="detectionSensorConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="DeviceConfigEntity" representedClassName="DeviceConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="buttonGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="buzzerGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="debugLogEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="disableTripleClick" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="doubleTapAsButtonPress" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="isManaged" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="ledHeartbeatEnabled" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<attribute name="nodeInfoBroadcastSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="rebroadcastMode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="role" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="serialEnabled" optional="YES" attributeType="Boolean" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="tripleClickAsAdHocPing" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<attribute name="tzdef" optional="YES" attributeType="String"/>
<relationship name="deviceConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="deviceConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="DeviceMetadataEntity" representedClassName="DeviceMetadataEntity" syncable="YES" codeGenerationType="class">
<attribute name="canShutdown" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="deviceStateVersion" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="excludedModules" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="firmwareVersion" optional="YES" attributeType="String"/>
<attribute name="hasBluetooth" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="hasEthernet" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="hasWifi" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="hwModel" optional="YES" attributeType="String"/>
<attribute name="positionFlags" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="role" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<relationship name="metadataNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="metadata" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="DisplayConfigEntity" representedClassName="DisplayConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="compassNorthTop" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="displayMode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="flipScreen" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="gpsFormat" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="headingBold" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<attribute name="oledType" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="screenCarouselInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="screenOnSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="units" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="wakeOnTapOrMotion" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<relationship name="displayConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="displayConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="ExternalNotificationConfigEntity" representedClassName="ExternalNotificationConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="active" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="alertBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="alertBellBuzzer" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="alertBellVibra" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="alertMessage" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="alertMessageBuzzer" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="alertMessageVibra" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="nagTimeout" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="output" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="outputBuzzer" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="outputMilliseconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="outputVibra" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="useI2SAsBuzzer" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="usePWM" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<relationship name="externalNotificationConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="externalNotificationConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="LocationEntity" representedClassName="LocationEntity" syncable="YES" codeGenerationType="class">
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="heading" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="id" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
<attribute name="latitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="longitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="speed" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="routeLocation" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RouteEntity" inverseName="locations" inverseEntity="RouteEntity"/>
</entity>
<entity name="LoRaConfigEntity" representedClassName="LoRaConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="bandwidth" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="channelNum" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="codingRate" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="frequencyOffset" optional="YES" attributeType="Float" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="hopLimit" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="ignoreMqtt" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="modemPreset" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="okToMqtt" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="overrideDutyCycle" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="overrideFrequency" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="regionCode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="spreadFactor" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="sx126xRxBoostedGain" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="txEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<attribute name="txPower" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="usePreset" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<relationship name="loRaConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="loRaConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="MessageEntity" representedClassName="MessageEntity" syncable="YES" codeGenerationType="class">
<attribute name="ackError" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="ackSNR" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="ackTimestamp" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="admin" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="adminDescription" optional="YES" attributeType="String"/>
<attribute name="channel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="isEmoji" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="messageId" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="messagePayload" optional="YES" attributeType="String" defaultValueString=""/>
<attribute name="messagePayloadMarkdown" optional="YES" attributeType="String"/>
<attribute name="messageTimestamp" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="pkiEncrypted" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="portNum" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="publicKey" optional="YES" attributeType="Binary"/>
<attribute name="read" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="realACK" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="receivedACK" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="replyID" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<relationship name="fromUser" optional="YES" maxCount="1" deletionRule="Nullify" ordered="YES" destinationEntity="UserEntity" inverseName="sentMessages" inverseEntity="UserEntity"/>
<relationship name="toUser" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="receivedMessages" inverseEntity="UserEntity"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="messageId"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="MQTTConfigEntity" representedClassName="MQTTConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="address" optional="YES" attributeType="String"/>
<attribute name="enabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="encryptionEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="jsonEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="mapPositionPrecision" optional="YES" attributeType="Integer 32" defaultValueString="13" usesScalarValueType="YES"/>
<attribute name="mapPublishIntervalSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="mapReportingEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="password" optional="YES" attributeType="String" maxValueString="30"/>
<attribute name="proxyToClientEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="root" optional="YES" attributeType="String" defaultValueString="msh"/>
<attribute name="tlsEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="username" optional="YES" attributeType="String" maxValueString="30"/>
<relationship name="mqttConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="mqttConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="MyInfoEntity" representedClassName="MyInfoEntity" syncable="YES" codeGenerationType="class">
<attribute name="adminIndex" optional="YES" attributeType="Integer 32" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="bleName" optional="YES" attributeType="String"/>
<attribute name="deviceId" optional="YES" attributeType="Binary"/>
<attribute name="minAppVersion" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="myNodeNum" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="peripheralId" optional="YES" attributeType="String"/>
<attribute name="rebootCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="registered" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<relationship name="channels" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="ChannelEntity" inverseName="myInfoChannel" inverseEntity="ChannelEntity"/>
<relationship name="myInfoNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="myInfo" inverseEntity="NodeInfoEntity"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="myNodeNum"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="NetworkConfigEntity" representedClassName="NetworkConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="dns" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="enabledProtocols" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="ethEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="gateway" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="ip" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="ntpServer" optional="YES" attributeType="String"/>
<attribute name="subnet" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="wifiEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="wifiMode" optional="YES" attributeType="Integer 32" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="wifiPsk" optional="YES" attributeType="String" minValueString="0" maxValueString="60"/>
<attribute name="wifiSsid" optional="YES" attributeType="String" minValueString="0" maxValueString="30"/>
<relationship name="networkConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="networkConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="NodeInfoEntity" representedClassName="NodeInfoEntity" syncable="YES" codeGenerationType="class">
<attribute name="bleName" optional="YES" attributeType="String"/>
<attribute name="channel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="favorite" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="firstHeard" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="hopsAway" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="ignored" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="lastHeard" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="peripheralId" optional="YES" attributeType="String"/>
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="sessionExpiration" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="sessionPasskey" optional="YES" attributeType="Binary"/>
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="viaMqtt" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<relationship name="ambientLightingConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="AmbientLightingConfigEntity" inverseName="ambientLightingConfigNode" inverseEntity="AmbientLightingConfigEntity"/>
<relationship name="bluetoothConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="BluetoothConfigEntity" inverseName="bluetoothConfigNode" inverseEntity="BluetoothConfigEntity"/>
<relationship name="cannedMessageConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="CannedMessageConfigEntity" inverseName="cannedMessagesConfigNode" inverseEntity="CannedMessageConfigEntity"/>
<relationship name="detectionSensorConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="DetectionSensorConfigEntity" inverseName="detectionSensorConfigNode" inverseEntity="DetectionSensorConfigEntity"/>
<relationship name="deviceConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="DeviceConfigEntity" inverseName="deviceConfigNode" inverseEntity="DeviceConfigEntity"/>
<relationship name="displayConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="DisplayConfigEntity" inverseName="displayConfigNode" inverseEntity="DisplayConfigEntity"/>
<relationship name="externalNotificationConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="ExternalNotificationConfigEntity" inverseName="externalNotificationConfigNode" inverseEntity="ExternalNotificationConfigEntity"/>
<relationship name="loRaConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="LoRaConfigEntity" inverseName="loRaConfigNode" inverseEntity="LoRaConfigEntity"/>
<relationship name="metadata" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DeviceMetadataEntity" inverseName="metadataNode" inverseEntity="DeviceMetadataEntity"/>
<relationship name="mqttConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MQTTConfigEntity" inverseName="mqttConfigNode" inverseEntity="MQTTConfigEntity"/>
<relationship name="myInfo" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="myInfoNode" inverseEntity="MyInfoEntity"/>
<relationship name="networkConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NetworkConfigEntity" inverseName="networkConfigNode" inverseEntity="NetworkConfigEntity"/>
<relationship name="pax" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PaxCounterEntity" inverseName="paxNode" inverseEntity="PaxCounterEntity"/>
<relationship name="paxCounterConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PaxCounterConfigEntity" inverseName="paxCounterConfigNode" inverseEntity="PaxCounterConfigEntity"/>
<relationship name="positionConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PositionConfigEntity" inverseName="positionConfigNode" inverseEntity="PositionConfigEntity"/>
<relationship name="positions" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PositionEntity" inverseName="nodePosition" inverseEntity="PositionEntity"/>
<relationship name="powerConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PowerConfigEntity" inverseName="powerConfigNode" inverseEntity="PowerConfigEntity"/>
<relationship name="rangeTestConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RangeTestConfigEntity" inverseName="rangeTestConfigNode" inverseEntity="RangeTestConfigEntity"/>
<relationship name="rtttlConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RTTTLConfigEntity" inverseName="rtttlConfigNode" inverseEntity="RTTTLConfigEntity"/>
<relationship name="securityConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SecurityConfigEntity" inverseName="securityConfigNode" inverseEntity="SecurityConfigEntity"/>
<relationship name="serialConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SerialConfigEntity" inverseName="serialConfigNode" inverseEntity="SerialConfigEntity"/>
<relationship name="storeForwardConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="StoreForwardConfigEntity" inverseName="storeForwardConfigNode" inverseEntity="StoreForwardConfigEntity"/>
<relationship name="telemetries" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TelemetryEntity" inverseName="nodeTelemetry" inverseEntity="TelemetryEntity"/>
<relationship name="telemetryConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TelemetryConfigEntity" inverseName="telemetryConfigNode" inverseEntity="TelemetryConfigEntity"/>
<relationship name="traceRoutes" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TraceRouteEntity" inverseName="node" inverseEntity="TraceRouteEntity"/>
<relationship name="user" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="userNode" inverseEntity="UserEntity"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="num"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="PaxCounterConfigEntity" representedClassName="PaxCounterConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="bleThreshold" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="updateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="wifiThreshold" optional="YES" attributeType="Integer 32" defaultValueString="-80" usesScalarValueType="YES"/>
<relationship name="paxCounterConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="paxCounterConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="PaxCounterEntity" representedClassName="PaxCounterEntity" syncable="YES" codeGenerationType="class">
<attribute name="ble" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="uptime" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="wifi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="paxNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="pax" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="PositionConfigEntity" representedClassName="PositionConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="broadcastSmartMinimumDistance" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="broadcastSmartMinimumIntervalSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="deviceGpsEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="fixedPosition" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="gpsAttemptTime" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="gpsEnGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="gpsMode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="gpsUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="positionBroadcastSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="positionFlags" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="rxGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="smartPositionEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="txGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="positionConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positionConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="PositionEntity" representedClassName="PositionEntity" syncable="YES" codeGenerationType="class">
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="heading" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="latest" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="latitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="longitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="precisionBits" optional="YES" attributeType="Integer 32" defaultValueString="32" usesScalarValueType="YES"/>
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="satsInView" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="seqNo" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="speed" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<relationship name="nodePosition" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positions" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="PowerConfigEntity" representedClassName="PowerConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="adcMultiplierOverride" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
<attribute name="deviceBatteryInaAddress" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="isPowerSaving" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="lsSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="minWakeSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="onBatteryShutdownAfterSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="waitBluetoothSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="powerConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="powerConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="RangeTestConfigEntity" representedClassName="RangeTestConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="save" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="sender" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
<relationship name="rangeTestConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="rangeTestConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="RouteEntity" representedClassName="RouteEntity" syncable="YES" codeGenerationType="class">
<attribute name="color" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="distance" optional="YES" attributeType="Double" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="elevationGain" optional="YES" attributeType="Double" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="endDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="id" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="name" optional="YES" attributeType="String"/>
<attribute name="notes" optional="YES" attributeType="String"/>
<relationship name="locations" optional="YES" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="LocationEntity" inverseName="routeLocation" inverseEntity="LocationEntity"/>
</entity>
<entity name="RTTTLConfigEntity" representedClassName="RTTTLConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="ringtone" optional="YES" attributeType="String" maxValueString="228" defaultValueString=""/>
<relationship name="rtttlConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="rtttlConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="SecurityConfigEntity" representedClassName="SecurityConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="adminChannelEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="adminKey" optional="YES" attributeType="Binary"/>
<attribute name="adminKey2" optional="YES" attributeType="Binary"/>
<attribute name="adminKey3" optional="YES" attributeType="Binary"/>
<attribute name="bluetoothLoggingEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="debugLogApiEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="isManaged" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="privateKey" optional="YES" attributeType="Binary"/>
<attribute name="publicKey" optional="YES" attributeType="Binary"/>
<attribute name="serialEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<relationship name="securityConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="securityConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="SerialConfigEntity" representedClassName="SerialConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="baudRate" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="echo" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="overrideConsoleSerialPort" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="rxd" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="timeout" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="txd" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="serialConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="serialConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="StoreForwardConfigEntity" representedClassName="StoreForwardConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="enabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="heartbeat" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<attribute name="historyReturnMax" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="historyReturnWindow" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="isRouter" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="lastHeartbeat" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="lastRequest" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="records" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="storeForwardConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="storeForwardConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="TelemetryConfigEntity" representedClassName="TelemetryConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="deviceUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="environmentDisplayFahrenheit" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="environmentMeasurementEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="environmentScreenEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="environmentUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="powerMeasurementEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="powerScreenEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="powerUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="telemetryConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetryConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="TelemetryEntity" representedClassName="TelemetryEntity" syncable="YES">
<attribute name="airUtilTx" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="barometricPressure" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="batteryLevel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="channelUtilization" optional="YES" attributeType="Float" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="current" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="gasResistance" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="iaq" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="irLux" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="lux" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="metricsType" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="numOnlineNodes" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="numPacketsRx" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="numPacketsRxBad" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="numPacketsTx" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="numRxDupe" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="numTotalNodes" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="numTxRelay" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="numTxRelayCanceled" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="powerCh1Current" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
<attribute name="powerCh1Voltage" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
<attribute name="powerCh2Current" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
<attribute name="powerCh2Voltage" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
<attribute name="powerCh3Current" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
<attribute name="powerCh3Voltage" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
<attribute name="radiation" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="rainfall1H" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="rainfall24H" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="relativeHumidity" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="soilMoisture" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="soilTemperature" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="temperature" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="uptimeSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="uvLux" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="voltage" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="weight" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="whiteLux" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="windDirection" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="windGust" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="windLull" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="windSpeed" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<relationship name="nodeTelemetry" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetries" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="TraceRouteEntity" representedClassName="TraceRouteEntity" syncable="YES" codeGenerationType="class">
<attribute name="hasPositions" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="hopsBack" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="hopsTowards" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="id" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="response" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="routeBackText" optional="YES" attributeType="String"/>
<attribute name="routeText" optional="YES" attributeType="String"/>
<attribute name="sent" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<relationship name="hops" optional="YES" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="TraceRouteHopEntity" inverseName="traceRoute" inverseEntity="TraceRouteHopEntity"/>
<relationship name="node" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="traceRoutes" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="TraceRouteHopEntity" representedClassName="TraceRouteHopEntity" syncable="YES" codeGenerationType="class">
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="back" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="latitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="longitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="name" optional="YES" attributeType="String"/>
<attribute name="num" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="time" attributeType="Date" usesScalarValueType="NO"/>
<relationship name="traceRoute" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TraceRouteEntity" inverseName="hops" inverseEntity="TraceRouteEntity"/>
</entity>
<entity name="UserEntity" representedClassName="UserEntity" syncable="YES" codeGenerationType="class">
<attribute name="hwDisplayName" optional="YES" attributeType="String"/>
<attribute name="hwModel" attributeType="String"/>
<attribute name="hwModelId" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="isLicensed" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="keyMatch" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<attribute name="lastMessage" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="longName" attributeType="String"/>
<attribute name="mute" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="newPublicKey" optional="YES" attributeType="Binary"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="numString" optional="YES" attributeType="String"/>
<attribute name="pkiEncrypted" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="publicKey" optional="YES" attributeType="Binary"/>
<attribute name="role" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="shortName" attributeType="String"/>
<attribute name="userId" attributeType="String"/>
<relationship name="receivedMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="toUser" inverseEntity="MessageEntity"/>
<relationship name="sentMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="fromUser" inverseEntity="MessageEntity"/>
<relationship name="userNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="user" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="WaypointEntity" representedClassName="WaypointEntity" syncable="YES" codeGenerationType="class">
<attribute name="created" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="expire" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="icon" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="lastUpdated" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="latitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="locked" attributeType="Integer 64" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="longDescription" optional="YES" attributeType="String" maxValueString="100"/>
<attribute name="longitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="name" attributeType="String" minValueString="1" maxValueString="30"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="id"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
</model>

View file

@ -42,5 +42,14 @@ public class TelemetryEntity: NSManagedObject, Identifiable {
@ManagedAttribute<Float>(attributeName: "windGust") public var windGust: Float?
@ManagedAttribute<Float>(attributeName: "windLull") public var windLull: Float?
@ManagedAttribute<Float>(attributeName: "windSpeed") public var windSpeed: Float?
@ManagedAttribute<Float>(attributeName: "irLux") public var irLux: Float?
@ManagedAttribute<Float>(attributeName: "lux") public var lux: Float?
@ManagedAttribute<Float>(attributeName: "uvLux") public var uvLux: Float?
@ManagedAttribute<Float>(attributeName: "whiteLux") public var whiteLux: Float?
@ManagedAttribute<Float>(attributeName: "radiation") public var radiation: Float?
@ManagedAttribute<Float>(attributeName: "rainfall1H") public var rainfall1H: Float?
@ManagedAttribute<Float>(attributeName: "rainfall24H") public var rainfall24H: Float?
@ManagedAttribute<Float>(attributeName: "soilTemperature") public var soilTemperature: Float?
@ManagedAttribute<UInt32>(attributeName: "soilMoisture") public var soilMoisture: UInt32?
}

View file

@ -38,12 +38,18 @@ class MetricsChartSeries: ObservableObject {
// Possibly converted to the proper units
let valueClosure: (TelemetryEntity) -> Float?
// Used for scaling the Y-axis
let initialYAxisRange: ClosedRange<Float>?
let minumumYAxisSpan: Float?
// Main initializer
init<Value, ChartBody: ChartContent, ForegroundStyle: ShapeStyle>(
id: String,
keyPath: KeyPath<TelemetryEntity, Value>,
name: String,
abbreviatedName: String,
initialYAxisRange: ClosedRange<Float>? = nil,
minumumYAxisSpan: Float? = nil,
conversion: ((Value) -> Value)? = nil,
visible: Bool = true,
foregroundStyle: @escaping ((ClosedRange<Float>?) -> ForegroundStyle?) = { _ in nil },
@ -54,6 +60,8 @@ class MetricsChartSeries: ObservableObject {
self.id = id
self.name = name
self.abbreviatedName = abbreviatedName
self.initialYAxisRange = initialYAxisRange
self.minumumYAxisSpan = minumumYAxisSpan
self.visible = visible
// By saving these closures, MetricsChartSeries can be type agnostic

View file

@ -7,6 +7,7 @@
import Foundation
import SwiftUI
class MetricsSeriesList: ObservableObject, RandomAccessCollection, RangeReplaceableCollection {
@Published var series: [MetricsChartSeries]
@ -38,25 +39,86 @@ class MetricsSeriesList: ObservableObject, RandomAccessCollection, RangeReplacea
return nil
}
// Calculates the chartRange based on the series configuration and data provided
// Besides checkign the range of the data, this function also obeys some series-level
// configuraiton, such as:
// 1. starting with a desired fixed range
// 2. obeying a minimum span
func chartRange(forData data: [TelemetryEntity]) -> ClosedRange<Float> {
var lower: Float?
var upper: Float?
var globalLower: Float = .infinity
var globalUpper: Float = -.infinity
// Keep track of the range of each series
var range: [MetricsChartSeries: ClosedRange<Float>] = [:]
// Determine if there is an initial fixed range.
// The range might exapand past this initial range if the data goes beyond.
for aSeries in self.visible {
if let thisRange = aSeries.initialYAxisRange {
range[aSeries] = thisRange
if thisRange.upperBound > globalUpper {globalUpper = thisRange.upperBound}
if thisRange.lowerBound < globalLower {globalLower = thisRange.lowerBound}
}
}
// Iterate through all the data. It would be easier to iterate
// the series then the data, but this way we only iterate the data once
for te in data {
for aSeries in self.visible {
var seriesUpper = range[aSeries]?.upperBound ?? -.infinity
var seriesLower = range[aSeries]?.lowerBound ?? .infinity
if let value = aSeries.valueFor(te) {
if value > (upper ?? -.infinity) {upper = value}
if value < (lower ?? .infinity) {lower = value}
// Update the global bounds
if value > globalUpper {globalUpper = value}
if value < globalLower {globalLower = value}
// Update the series bounds if necessary
if value > seriesUpper || value < seriesLower {
if value > seriesUpper {
seriesUpper = value
}
if value < seriesLower {
seriesLower = value
}
if seriesUpper.isFinite && seriesLower.isFinite {
range[aSeries] = seriesLower...seriesUpper
}
}
}
}
}
// Return default range if no data or nil
guard let lower, let upper else {
// Go through each series one last time to obey the minimum span
for aSeries in self.visible {
if let minimumSpan = aSeries.minumumYAxisSpan,
let currentRange = range[aSeries] {
let currentSpan = currentRange.upperBound - currentRange.lowerBound
//Logger.data.info("Updated \(aSeries.id) to \(range[aSeries] ?? 0...0) span=\(currentSpan)")
if currentSpan < minimumSpan {
// Calculate the center of the range
let centerOfRange = currentRange.lowerBound + (currentSpan / 2)
let newLower = centerOfRange - (minimumSpan / 2.0)
let newUpper = centerOfRange + (minimumSpan / 2.0)
if newUpper > globalUpper {
globalUpper = newUpper
}
if newLower < globalLower {
globalLower = newLower
}
}
}
}
// Return default range if no data
if !globalLower.isFinite || !globalUpper.isFinite {
return 0.0...100.0
}
return lower...upper
return globalLower...globalUpper
}
// Collection conformance
typealias Index = Int
typealias Element = MetricsChartSeries

View file

@ -103,123 +103,4 @@ extension NSPersistentContainer {
case invalidSource(String)
}
/// Restore backup persistent stores located in the directory referenced by `backupURL`.
/// **Be very careful with this**. To restore a persistent store, the current persistent store must be removed from the container. When that happens, **all currently loaded Core Data objects** will become invalid. Using them after restoring will cause your app to crash.
/// When calling this method you **must** ensure that you do not continue to use any previously fetched managed objects or existing fetched results controllers. **If this method does not throw, that does not mean your app is safe.** You need to take extra steps to prevent crashes. The details vary depending on the nature of your app.
/// - Parameter backupURL: A file URL containing backup copies of all currently loaded persistent stores.
/// - Throws: `CopyPersistentStoreError` in various situations.
/// - Returns: Nothing. If no errors are thrown, the restore is complete.
func restorePersistentStore(from backupURL: URL) throws {
guard backupURL.isFileURL else {
throw CopyPersistentStoreErrors.invalidSource("Backup URL must be a file URL")
}
var isDirectory: ObjCBool = false
if FileManager.default.fileExists(atPath: backupURL.path, isDirectory: &isDirectory) {
if !isDirectory.boolValue {
throw CopyPersistentStoreErrors.invalidSource("Source URL must be a directory")
}
} else {
throw CopyPersistentStoreErrors.invalidSource("Source URL must exist")
}
for persistentStoreDescription in persistentStoreDescriptions {
guard let loadedStoreURL = persistentStoreDescription.url else {
continue
}
let backupStoreURL = backupURL.appendingPathComponent(loadedStoreURL.lastPathComponent)
guard FileManager.default.fileExists(atPath: backupStoreURL.path) else {
throw CopyPersistentStoreErrors.invalidSource("Missing backup store for \(backupStoreURL)")
}
do {
let storeOptions = persistentStoreDescription.options
let configurationName = persistentStoreDescription.configuration
let storeType = persistentStoreDescription.type
// Replace the current store with the backup copy. This has a side effect of removing the current store from the Core Data stack.
// When restoring, it's necessary to use the current persistent store coordinator.
try persistentStoreCoordinator.replacePersistentStore(at: loadedStoreURL, destinationOptions: storeOptions, withPersistentStoreFrom: backupStoreURL, sourceOptions: storeOptions, ofType: storeType)
// Add the persistent store at the same location we've been using, because it was removed in the previous step.
try persistentStoreCoordinator.addPersistentStore(ofType: storeType, configurationName: configurationName, at: loadedStoreURL, options: storeOptions)
} catch {
throw CopyPersistentStoreErrors.copyStoreError("Could not restore: \(error.localizedDescription)")
}
}
}
/// Copy all loaded persistent stores to a new directory. Each currently loaded file-based persistent store will be copied (including journal files, external binary storage, and anything else Core Data needs) into the destination directory to a persistent store with the same name and type as the existing store. In-memory stores, if any, are skipped.
/// - Parameters:
/// - destinationURL: Destination for new persistent store files. Must be a file URL. If `overwriting` is `false` and `destinationURL` exists, it must be a directory.
/// - overwriting: If `true`, any existing copies of the persistent store will be replaced or updated. If `false`, existing copies will not be changed or remoted. When this is `false`, the destination persistent store file must not already exist.
/// - Throws: `CopyPersistentStoreError`
/// - Returns: Nothing. If no errors are thrown, all loaded persistent stores will be copied to the destination directory.
func copyPersistentStores(to destinationURL: URL, overwriting: Bool = false) throws {
guard !destinationURL.relativeString.contains("/0/") else {
throw CopyPersistentStoreErrors.invalidDestination("Invalid 0 Node Id")
}
guard destinationURL.isFileURL else {
throw CopyPersistentStoreErrors.invalidDestination("Destination URL must be a file URL")
}
// If the destination exists and we aren't overwriting it, then it must be a directory. (If we are overwriting, we'll remove it anyway, so it doesn't matter whether it's a directory).
var isDirectory: ObjCBool = false
if !overwriting && FileManager.default.fileExists(atPath: destinationURL.path, isDirectory: &isDirectory) {
if !isDirectory.boolValue {
throw CopyPersistentStoreErrors.invalidDestination("Destination URL must be a directory")
}
// Don't check if destination stores exist in the destination dir, that comes later on a per-store basis.
}
// If we're overwriting, remove the destination.
if overwriting && FileManager.default.fileExists(atPath: destinationURL.path) {
do {
try FileManager.default.removeItem(at: destinationURL)
} catch {
throw CopyPersistentStoreErrors.destinationNotRemoved("Can't overwrite destination at \(destinationURL)")
}
}
// Create the destination directory
do {
try FileManager.default.createDirectory(at: destinationURL, withIntermediateDirectories: true, attributes: nil)
} catch {
throw CopyPersistentStoreErrors.destinationError("Could not create destination directory at \(destinationURL)")
}
for persistentStoreDescription in persistentStoreDescriptions {
guard let storeURL = persistentStoreDescription.url else {
continue
}
guard persistentStoreDescription.type != NSInMemoryStoreType else {
continue
}
let destinationStoreURL = destinationURL.appendingPathComponent(storeURL.lastPathComponent)
if !overwriting && FileManager.default.fileExists(atPath: destinationStoreURL.path) {
// If the destination exists, the replacePersistentStore call will update it in place. That's fine unless we're not overwriting.
throw CopyPersistentStoreErrors.destinationError("Destination already exists at \(destinationStoreURL)")
}
do {
// Replace an existing backup, if any, with a new one with the same options and type. This doesn't affect the current Core Data stack.
// The function name says "replace", but it works if there's nothing at the destination yet. In that case it creates a new persistent store.
// Note that for backup, it doesn't matter if the persistent store coordinator is the one currently in use or a different one. It could be a class function, for this use.
try persistentStoreCoordinator.replacePersistentStore(at: destinationStoreURL, destinationOptions: persistentStoreDescription.options, withPersistentStoreFrom: storeURL, sourceOptions: persistentStoreDescription.options, ofType: persistentStoreDescription.type)
/// Cleanup extra files
let directory = destinationStoreURL.deletingLastPathComponent()
/// Delete -wal file
do {
try FileManager.default.removeItem(at: directory.appendingPathComponent("Meshtastic.sqlite-wal"))
/// Delete -shm file
do {
try FileManager.default.removeItem(at: directory.appendingPathComponent("Meshtastic.sqlite-shm"))
} catch {
Logger.services.error("🗄 Error Deleting Meshtastic.sqlite-shm file \(error, privacy: .public)")
}
} catch {
Logger.services.error("🗄 Error Deleting Meshtastic.sqlite-wal file \(error, privacy: .public)")
}
} catch {
Logger.services.error("🗄 Error Deleting Meshtastic.sqlite file \(error, privacy: .public)")
throw CopyPersistentStoreErrors.copyStoreError("\(error.localizedDescription)")
}
}
}
}

View file

@ -658,12 +658,14 @@ func upsertNetworkConfigPacket(config: Config.NetworkConfig, nodeNum: Int64, ses
newNetworkConfig.wifiSsid = config.wifiSsid
newNetworkConfig.wifiPsk = config.wifiPsk
newNetworkConfig.ethEnabled = config.ethEnabled
newNetworkConfig.enabledProtocols = Int32(config.enabledProtocols)
fetchedNode[0].networkConfig = newNetworkConfig
} else {
fetchedNode[0].networkConfig?.ethEnabled = config.ethEnabled
fetchedNode[0].networkConfig?.wifiEnabled = config.wifiEnabled
fetchedNode[0].networkConfig?.wifiSsid = config.wifiSsid
fetchedNode[0].networkConfig?.wifiPsk = config.wifiPsk
fetchedNode[0].networkConfig?.enabledProtocols = Int32(config.enabledProtocols)
}
if sessionPasskey != nil {
fetchedNode[0].sessionPasskey = sessionPasskey

View file

@ -87,7 +87,8 @@
"images": [
"t-echo.svg"
],
"requiresDfu": true
"requiresDfu": true,
"hasInkHud": true
},
{
"hwModel": 8,
@ -153,17 +154,21 @@
"images": [
"tbeam-s3-core.svg"
],
"requiresDfu": true
"requiresDfu": true,
"partitionScheme": "8MB"
},
{
"hwModel": 13,
"hwModelSlug": "RAK11200",
"platformioTarget": "rak11200",
"architecture": "esp32",
"activelySupported": false,
"activelySupported": true,
"displayName": "RAK WisBlock 11200",
"tags": [
"RAK"
],
"images": [
"rak11200.svg"
]
},
{
@ -188,7 +193,7 @@
"displayName": "LILYGO T-LoRa V2.1-1.8",
"tags": [
"LilyGo",
"2.4G LoRA"
"2.4GHz"
],
"images": [
"tlora-v2-1-1_8.svg"
@ -338,7 +343,8 @@
"requiresDfu": true,
"images": [
"station-g2.svg"
]
],
"partitionScheme": "16MB"
},
{
"hwModel": 39,
@ -404,7 +410,8 @@
"images": [
"heltec-v3.svg",
"heltec-v3-case.svg"
]
],
"partitionScheme": "8MB"
},
{
"hwModel": 44,
@ -419,7 +426,8 @@
],
"images": [
"heltec-wsl-v3.svg"
]
],
"partitionScheme": "8MB"
},
{
"hwModel": 47,
@ -469,7 +477,8 @@
"images": [
"heltec-wireless-tracker.svg"
],
"requiresDfu": true
"requiresDfu": true,
"partitionScheme": "8MB"
},
{
"hwModel": 58,
@ -482,7 +491,8 @@
"images": [
"heltec-wireless-tracker.svg"
],
"requiresDfu": true
"requiresDfu": true,
"partitionScheme": "8MB"
},
{
"hwModel": 49,
@ -497,7 +507,9 @@
],
"images": [
"heltec-wireless-paper.svg"
]
],
"hasInkHud": true,
"partitionScheme": "8MB"
},
{
"hwModel": 50,
@ -513,7 +525,9 @@
"images": [
"t-deck.svg"
],
"requiresDfu": true
"requiresDfu": true,
"hasMui": true,
"partitionScheme": "16MB"
},
{
"hwModel": 51,
@ -528,7 +542,8 @@
],
"images": [
"t-watch-s3.svg"
]
],
"partitionScheme": "16MB"
},
{
"hwModel": 52,
@ -537,7 +552,9 @@
"architecture": "esp32-s3",
"activelySupported": true,
"supportLevel": 3,
"displayName": "Pi Computer S3"
"displayName": "Pi Computer S3",
"hasMui": true,
"partitionScheme": "8MB"
},
{
"hwModel": 53,
@ -567,7 +584,8 @@
"displayName": "Heltec Wireless Paper V1.0",
"images": [
"heltec-wireless-paper-v1_0.svg"
]
],
"partitionScheme": "8MB"
},
{
"hwModel": 59,
@ -577,36 +595,41 @@
"activelySupported": true,
"supportLevel": 3,
"displayName": "unPhone",
"requiresDfu": true
"requiresDfu": true,
"hasMui": true,
"partitionScheme": "8MB"
},
{
"hwModel": 48,
"hwModelSlug": "HELTEC_WIRELESS_TRACKER",
"platformioTarget": "tracksenger",
"architecture": "esp32-s3",
"activelySupported": true,
"activelySupported": false,
"supportLevel": 3,
"displayName": "TrackSenger (small TFT)",
"requiresDfu": true
"requiresDfu": true,
"partitionScheme": "8MB"
},
{
"hwModel": 48,
"hwModelSlug": "HELTEC_WIRELESS_TRACKER",
"platformioTarget": "tracksenger-lcd",
"architecture": "esp32-s3",
"activelySupported": true,
"activelySupported": false,
"supportLevel": 3,
"displayName": "TrackSenger (big TFT)",
"requiresDfu": true
"requiresDfu": true,
"partitionScheme": "8MB"
},
{
"hwModel": 48,
"hwModelSlug": "HELTEC_WIRELESS_TRACKER",
"platformioTarget": "tracksenger-oled",
"architecture": "esp32-s3",
"activelySupported": true,
"activelySupported": false,
"supportLevel": 3,
"displayName": "TrackSenger (big OLED)"
"displayName": "TrackSenger (big OLED)",
"partitionScheme": "8MB"
},
{
"hwModel": 61,
@ -647,7 +670,8 @@
"images": [
"heltec-vision-master-t190.svg"
],
"requiresDfu": true
"requiresDfu": true,
"partitionScheme": "8MB"
},
{
"hwModel": 67,
@ -663,7 +687,9 @@
"images": [
"heltec-vision-master-e213.svg"
],
"requiresDfu": true
"requiresDfu": true,
"hasInkHud": true,
"partitionScheme": "8MB"
},
{
"hwModel": 68,
@ -679,7 +705,9 @@
"images": [
"heltec-vision-master-e290.svg"
],
"requiresDfu": true
"requiresDfu": true,
"hasInkHud": true,
"partitionScheme": "8MB"
},
{
"hwModel": 69,
@ -711,7 +739,9 @@
],
"images": [
"seeed-sensecap-indicator.svg"
]
],
"hasMui": true,
"partitionScheme": "8MB"
},
{
"hwModel": 71,
@ -730,8 +760,8 @@
"requiresDfu": true
},
{
"hwModel": 72,
"hwModelSlug": "Seeed_XIAO_S3",
"hwModel": 81,
"hwModelSlug": "SEEED_XIAO_S3",
"platformioTarget": "seeed-xiao-s3",
"architecture": "esp32-s3",
"activelySupported": true,
@ -743,14 +773,15 @@
"images": [
"seeed-xiao-s3.svg"
],
"requiresDfu": true
"requiresDfu": true,
"partitionScheme": "8MB"
},
{
"hwModel": 84,
"hwModelSlug": "WISMESH_TAP",
"platformioTarget": "rak_wismeshtap",
"architecture": "nrf52840",
"activelySupported": false,
"activelySupported": true,
"supportLevel": 1,
"displayName": "RAK WisMesh Tap",
"tags": [
@ -760,5 +791,79 @@
"rak-wismeshtap.svg"
],
"requiresDfu": true
},
{
"hwModel": 22,
"hwModelSlug": "WISMESH_HUB",
"platformioTarget": "rak2560",
"architecture": "nrf52840",
"activelySupported": true,
"supportLevel": 1,
"displayName": "RAK WisMesh Repeater",
"tags": [
"RAK"
],
"images": [
"rak2560.svg"
],
"requiresDfu": true
},
{
"hwModel": 63,
"hwModelSlug": "NRF52_PROMICRO_DIY",
"platformioTarget": "nrf52_promicro_diy_tcxo",
"architecture": "nrf52840",
"activelySupported": true,
"supportLevel": 3,
"displayName": "NRF52 Pro-micro DIY",
"tags": [
"DIY"
],
"images": [
"promicro.svg"
],
"requiresDfu": true
},
{
"hwModel": 88,
"hwModelSlug": "XIAO_NRF52_KIT",
"platformioTarget": "seeed_xiao_nrf52840_kit",
"architecture": "nrf52840",
"activelySupported": true,
"supportLevel": 1,
"displayName": "Seeed Xiao NRF52840 Kit",
"tags": [
"Seeed"
],
"requiresDfu": true,
"images": [
"seeed_xiao_nrf52_kit.svg"
]
},
{
"hwModel": 89,
"hwModelSlug": "THINKNODE_M1",
"platformioTarget": "thinknode_m1",
"architecture": "nrf52840",
"activelySupported": false,
"supportLevel": 1,
"displayName": "ThinkNode M1",
"tags": [
"Elecrow"
],
"requiresDfu": true
},
{
"hwModel": 90,
"hwModelSlug": "THINKNODE_M2",
"platformioTarget": "thinknode_m2",
"architecture": "esp32-s3",
"activelySupported": false,
"supportLevel": 1,
"displayName": "ThinkNode M2",
"tags": [
"Elecrow"
],
"requiresDfu": false
}
]

View file

@ -61,9 +61,9 @@ struct Connect: View {
.padding(.trailing)
VStack(alignment: .leading) {
if node != nil {
Text(connectedPeripheral.longName).font(.title2)
Text(connectedPeripheral.longName.addingVariationSelectors).font(.title2)
}
Text("BLE Name").font(.callout)+Text(": \(bleManager.connectedPeripheral?.peripheral.name ?? "unknown".localized)")
Text("BLE Name").font(.callout)+Text(": \(bleManager.connectedPeripheral?.peripheral.name?.addingVariationSelectors ?? "unknown".localized)")
.font(.callout).foregroundColor(Color.gray)
if node != nil {
Text("firmware.version").font(.callout)+Text(": \(node?.metadata?.firmwareVersion ?? "unknown".localized)")
@ -120,7 +120,7 @@ struct Connect: View {
#endif
Text("Num: \(String(node!.num))")
Text("Short Name: \(node?.user?.shortName ?? "?")")
Text("Long Name: \(node?.user?.longName ?? "unknown".localized)")
Text("Long Name: \(node?.user?.longName?.addingVariationSelectors ?? "unknown".localized)")
Text("BLE RSSI: \(connectedPeripheral.rssi)")
Button {
@ -214,18 +214,6 @@ struct Connect: View {
if let connectedPeripheral = bleManager.connectedPeripheral, connectedPeripheral.peripheral.state == CBPeripheralState.connected {
bleManager.disconnectPeripheral()
}
let container = NSPersistentContainer(name: "Meshtastic")
guard let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
Logger.data.error("nil File path for back")
return
}
do {
try container.copyPersistentStores(to: url.appendingPathComponent("backup").appendingPathComponent("\(UserDefaults.preferredPeripheralNum)"), overwriting: true)
Logger.data.notice("🗂️ Made a core data backup to backup/\(UserDefaults.preferredPeripheralNum)")
} catch {
Logger.data.error("🗂️ Core data backup copy error: \(error, privacy: .public)")
}
clearCoreDataDatabase(context: context, includeRoutes: false)
}
UserDefaults.preferredPeripheralId = selectedPeripherialId
@ -333,7 +321,7 @@ struct Connect: View {
let localStats = node?.telemetries?.filtered(using: NSPredicate(format: "metricsType == 4"))
let mostRecent = localStats?.lastObject as? TelemetryEntity
let activityAttributes = MeshActivityAttributes(nodeNum: Int(node?.num ?? 0), name: node?.user?.longName ?? "unknown")
let activityAttributes = MeshActivityAttributes(nodeNum: Int(node?.num ?? 0), name: node?.user?.longName?.addingVariationSelectors ?? "unknown")
let future = Date(timeIntervalSinceNow: Double(timerSeconds))
let initialContentState = MeshActivityAttributes.ContentState(uptimeSeconds: UInt32(mostRecent?.uptimeSeconds ?? 0),

View file

@ -53,6 +53,5 @@ struct ContentView: View {
}
.tag(NavigationState.Tab.settings)
}
.toolbarBackground(.visible, for: .tabBar)
}
}

View file

@ -16,7 +16,7 @@ struct CircleText: View {
Circle()
.fill(color)
.frame(width: circleSize, height: circleSize)
Text(text)
Text(text.addingVariationSelectors)
.frame(width: circleSize * 0.9, height: circleSize * 0.9, alignment: .center)
.foregroundColor(color.isLight() ? .black : .white)
.minimumScaleFactor(0.001)

View file

@ -0,0 +1,47 @@
//
// CompactWidget.swift
// Meshtastic
//
// Created by Jake Bordens on 3/14/25.
//
import SwiftUI
// This file was created for the purpose of previewing
// all of the Compact Widgets in one place.
// In the future, it could be used for a CompactWidget superclass, if desired.
#Preview {
let gridItemLayout = Array(repeating: GridItem(.flexible(), spacing: 10), count: 2)
Form {
LazyVGrid(columns: gridItemLayout) {
HumidityCompactWidget(humidity: 27, dewPoint: "32°")
HumidityCompactWidget(humidity: 27, dewPoint: nil)
WeatherConditionsCompactWidget(temperature: "24°F", symbolName: "sun.rain.fill", description: "Raining")
PressureCompactWidget(pressure: "1004.76", unit: "hPA", low: true)
PressureCompactWidget(pressure: "1004.76", unit: "hPA", low: false)
WindCompactWidget(speed: "12 mph", gust: "15 mph", direction: "SW")
WindCompactWidget(speed: "12 mph", gust: nil, direction: "SW")
WindCompactWidget(speed: "12 mph", gust: "15 mph", direction: nil)
WindCompactWidget(speed: "12 mph", gust: nil, direction: nil)
RadiationCompactWidget(radiation: "15", unit: "µR/hr")
DistanceCompactWidget(distance: "123", unit: "mm")
WeightCompactWidget(weight: "123", unit: "kg")
SoilTemperatureCompactWidget(temperature: "23", unit: "°C")
SoilMoistureCompactWidget(moisture: "23", unit: "%")
let rain: Float = 10.1
let locale = NSLocale.current as NSLocale
let usesMetricSystem = locale.usesMetricSystem // Returns true for metric (mm), false for imperial (inches)
let unit = usesMetricSystem ? UnitLength.millimeters : UnitLength.inches
let unitLabel = usesMetricSystem ? "mm" : "in"
let measurement = Measurement(value: Double(rain), unit: UnitLength.millimeters)
let decimals = usesMetricSystem ? 0 : 1
let formattedRain = measurement.converted(to: unit).value.formatted(.number.precision(.fractionLength(decimals)))
RainfallCompactWidget(timespan: .rainfall1H, rainfall: formattedRain, unit: unitLabel)
RainfallCompactWidget(timespan: .rainfall24H, rainfall: formattedRain, unit: unitLabel)
}
}
}

View file

@ -17,6 +17,7 @@ struct CurrentConditionsCompact: View {
.symbolRenderingMode(.multicolor)
}
}
struct CurrentConditionsCompact_Previews: PreviewProvider {
static var previews: some View {

View file

@ -0,0 +1,39 @@
//
// DistanceCompactWidget.swift
// Meshtastic
//
// Created by Jake Bordens on 3/14/25.
//
import SwiftUI
struct DistanceCompactWidget: View {
let distance: String
let unit: String
var body: some View {
VStack(alignment: .leading) {
HStack(alignment: .firstTextBaseline) {
Image(systemName: "ruler")
.imageScale(.small)
.foregroundColor(.accentColor)
Text("Distance")
.textCase(.uppercase)
.font(.callout)
}
HStack {
Text("\(distance)")
.font(distance.length < 4 ? .system(size: 50) : .system(size: 40) )
Text(unit)
.font(.system(size: 14))
}
}
.frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140)
.padding()
.background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous))
}
}
#Preview {
DistanceCompactWidget(distance: "123", unit: "mm")
}

View file

@ -0,0 +1,47 @@
//
// HumidityCompactWidget.swift
// Meshtastic
//
// Created by Jake Bordens on 3/14/25.
//
import SwiftUI
struct HumidityCompactWidget: View {
let humidity: Int
let dewPoint: String?
var body: some View {
VStack(alignment: .leading) {
HStack(spacing: 5.0) {
Image(systemName: "humidity")
.foregroundColor(.accentColor)
.font(.callout)
Text("Humidity")
.textCase(.uppercase)
.font(.caption)
}
Text("\(humidity)%")
.font(.largeTitle)
.padding(.bottom, 5)
if let dewPoint {
Text("The dew point is \(dewPoint) right now.")
.lineLimit(3)
.allowsTightening(true)
.fixedSize(horizontal: false, vertical: true)
.font(.caption2)
}
}
.frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140)
.padding()
.background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous))
}
}
#Preview {
let gridItemLayout = Array(repeating: GridItem(.flexible(), spacing: 10), count: 2)
Form {
LazyVGrid(columns: gridItemLayout) {
HumidityCompactWidget(humidity: 27, dewPoint: "32°")
HumidityCompactWidget(humidity: 27, dewPoint: nil)
}
}
}

View file

@ -0,0 +1,43 @@
//
// PressureCompactWidget.swift
// Meshtastic
//
// Created by Jake Bordens on 3/14/25.
//
import SwiftUI
struct PressureCompactWidget: View {
let pressure: String
let unit: String
let low: Bool
var body: some View {
VStack(alignment: .leading) {
HStack(spacing: 5.0) {
Image(systemName: "gauge")
.foregroundColor(.accentColor)
.font(.callout)
Text("Pressure")
.textCase(.uppercase)
.font(.caption)
}
Text(pressure)
.font(pressure.length < 7 ? .system(size: 35) : .system(size: 30) )
Text(low ? "LOW" : "HIGH")
.padding(.bottom, 10)
Text(unit)
}
.frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140)
.padding()
.background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous))
}
}
#Preview {
let gridItemLayout = Array(repeating: GridItem(.flexible(), spacing: 10), count: 2)
Form {
LazyVGrid(columns: gridItemLayout) {
PressureCompactWidget(pressure: "1004.76", unit: "hPA", low: true)
PressureCompactWidget(pressure: "1004.76", unit: "hPA", low: false)
}
}
}

View file

@ -0,0 +1,39 @@
//
// RadiationCompactWidget.swift
// Meshtastic
//
// Created by Jake Bordens on 3/14/25.
//
import SwiftUI
struct RadiationCompactWidget: View {
let radiation: String
let unit: String
var body: some View {
VStack(alignment: .leading) {
HStack(alignment: .firstTextBaseline) {
Text(verbatim: "")
.font(.system(size: 30, design: .monospaced))
.foregroundColor(.accentColor)
Text("Radiation")
.textCase(.uppercase)
.font(.callout)
}
HStack {
Text("\(radiation)")
.font(radiation.length < 4 ? .system(size: 50) : .system(size: 34) )
Text(unit)
.font(.system(size: 14))
}
}
.frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140)
.padding()
.background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous))
}
}
#Preview {
RadiationCompactWidget(radiation: "15", unit: "µR/hr")
}

View file

@ -0,0 +1,66 @@
//
// RainfallCompactWidgets.swift
// Meshtastic
//
// Created by Jake Bordens on 3/15/25.
//
import SwiftUI
struct RainfallCompactWidget: View {
enum RainfallTimeSpan: String {
case rainfall1H = "Rainfall 1H"
case rainfall24H = "Rainfall 24H"
}
let timespan: RainfallTimeSpan
let rainfall: String
let unit: String
private var icon: Image {
if timespan == .rainfall1H {
return Image(systemName: "cloud.rain.fill")
}
return Image(systemName: "cloud.heavyrain.fill")
}
var body: some View {
VStack(alignment: .leading) {
HStack(alignment: .firstTextBaseline) {
icon.imageScale(.small)
.foregroundColor(.accentColor)
Text(timespan.rawValue)
.textCase(.uppercase)
.font(.callout)
}
HStack {
Text("\(rainfall)")
.font(rainfall.length < 4 ? .system(size: 50) : .system(size: 40) )
Text(unit)
.font(.system(size: 14))
}
}
.frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140)
.padding()
.background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous))
}
}
#Preview {
let gridItemLayout = Array(repeating: GridItem(.flexible(), spacing: 10), count: 2)
Form {
LazyVGrid(columns: gridItemLayout) {
let rain: Float = 10.1
let locale = NSLocale.current as NSLocale
let usesMetricSystem = locale.usesMetricSystem // Returns true for metric (mm), false for imperial (inches)
let unit = usesMetricSystem ? UnitLength.millimeters : UnitLength.inches
let unitLabel = usesMetricSystem ? "mm" : "in"
let measurement = Measurement(value: Double(rain), unit: UnitLength.millimeters)
let decimals = usesMetricSystem ? 0 : 1
let formattedRain = measurement.converted(to: unit).value.formatted(.number.precision(.fractionLength(decimals)))
RainfallCompactWidget(timespan: .rainfall1H, rainfall: formattedRain, unit: unitLabel)
RainfallCompactWidget(timespan: .rainfall24H, rainfall: formattedRain, unit: unitLabel)
}
}
}

View file

@ -0,0 +1,72 @@
//
// SoilCompactWidgets.swift
// Meshtastic
//
// Created by Jake Bordens on 3/14/25.
//
import SwiftUI
struct SoilTemperatureCompactWidget: View {
let temperature: String
let unit: String
var body: some View {
VStack(alignment: .leading) {
HStack(alignment: .firstTextBaseline) {
Image("soil.temperature")
.imageScale(.small)
.foregroundColor(.accentColor)
Text("Soil Temp")
.textCase(.uppercase)
.font(.callout)
}
HStack {
Text("\(temperature)")
.font(temperature.length < 4 ? .system(size: 50) : .system(size: 40) )
Text(unit)
.font(.system(size: 14))
}
}
.frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140)
.padding()
.background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous))
}
}
struct SoilMoistureCompactWidget: View {
let moisture: String
let unit: String
var body: some View {
VStack(alignment: .leading) {
HStack(alignment: .firstTextBaseline) {
Image("soil.moisture")
.imageScale(.small)
.foregroundColor(.accentColor)
Text("Soil Moisture")
.textCase(.uppercase)
.font(.callout)
}
HStack {
Text("\(moisture)")
.font(moisture.length < 4 ? .system(size: 50) : .system(size: 40) )
Text(unit)
.font(.system(size: 14))
}
}
.frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140)
.padding()
.background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous))
}
}
#Preview {
let gridItemLayout = Array(repeating: GridItem(.flexible(), spacing: 10), count: 2)
Form {
LazyVGrid(columns: gridItemLayout) {
SoilTemperatureCompactWidget(temperature: "23", unit: "°C")
SoilMoistureCompactWidget(moisture: "23", unit: "%")
}
}
}

View file

@ -0,0 +1,42 @@
//
// WeatherConditionsCompactWidget.swift
// Meshtastic
//
// Created by Jake Bordens on 3/14/25.
//
import SwiftUI
struct WeatherConditionsCompactWidget: View {
let temperature: String
let symbolName: String
let description: String
var body: some View {
VStack(alignment: .leading) {
HStack(spacing: 5.0) {
Image(systemName: symbolName)
.foregroundColor(.accentColor)
.font(.callout)
Text(description)
.lineLimit(2)
.allowsTightening(/*@START_MENU_TOKEN@*/true/*@END_MENU_TOKEN@*/)
.fixedSize(horizontal: false, vertical: true)
.font(.caption)
}
Text(temperature)
.font(temperature.length < 4 ? .system(size: 72) : .system(size: 54) )
}
.frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140)
.padding()
.background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous))
}
}
#Preview {
let gridItemLayout = Array(repeating: GridItem(.flexible(), spacing: 10), count: 2)
Form {
LazyVGrid(columns: gridItemLayout) {
WeatherConditionsCompactWidget(temperature: "24°F", symbolName: "sun.rain.fill", description: "Raining")
}
}
}

View file

@ -0,0 +1,39 @@
//
// WeightCompactWidget.swift
// Meshtastic
//
// Created by Jake Bordens on 3/14/25.
//
import SwiftUI
struct WeightCompactWidget: View {
let weight: String
let unit: String
var body: some View {
VStack(alignment: .leading) {
HStack(alignment: .firstTextBaseline) {
Image(systemName: "scalemass")
.imageScale(.small)
.foregroundColor(.accentColor)
Text("Weight")
.textCase(.uppercase)
.font(.callout)
}
HStack {
Text("\(weight)")
.font(weight.length < 4 ? .system(size: 50) : .system(size: 40) )
Text(unit)
.font(.system(size: 14))
}
}
.frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140)
.padding()
.background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous))
}
}
#Preview {
WeightCompactWidget(weight: "123", unit: "kg")
}

View file

@ -0,0 +1,45 @@
//
// WindCompactWidget.swift
// Meshtastic
//
// Created by Jake Bordens on 3/14/25.
//
import SwiftUI
struct WindCompactWidget: View {
let speed: String
let gust: String?
let direction: String?
var body: some View {
let hasGust = ((gust ?? "").isEmpty == false)
VStack(alignment: .leading) {
Label { Text("Wind").textCase(.uppercase) } icon: { Image(systemName: "wind").foregroundColor(.accentColor) }
if let direction {
Text("\(direction)")
.font(!hasGust ? .callout : .caption)
.padding(.bottom, 10)
}
Text(speed)
.font(.system(size: 35))
if let gust, !gust.isEmpty {
Text("Gusts \(gust)")
}
}
.frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140)
.padding()
.background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous))
}
}
#Preview {
let gridItemLayout = Array(repeating: GridItem(.flexible(), spacing: 10), count: 2)
Form {
LazyVGrid(columns: gridItemLayout) {
WindCompactWidget(speed: "12 mph", gust: "15 mph", direction: "SW")
WindCompactWidget(speed: "12 mph", gust: nil, direction: "SW")
WindCompactWidget(speed: "12 mph", gust: "15 mph", direction: nil)
WindCompactWidget(speed: "12 mph", gust: nil, direction: nil)
}
}
}

View file

@ -28,7 +28,7 @@ struct ConnectedDevice: View {
.imageScale(.large)
.foregroundColor(.green)
.symbolRenderingMode(.hierarchical)
Text(name).font(name.isEmoji() ? .title : .callout).foregroundColor(.gray)
Text(name.addingVariationSelectors).font(name.isEmoji() ? .title : .callout).foregroundColor(.gray)
} else {
Image(systemName: "antenna.radiowaves.left.and.right.slash")
.imageScale(.medium)

View file

@ -89,113 +89,6 @@ struct LocalWeatherConditions: View {
}
}
struct WeatherConditionsCompactWidget: View {
let temperature: String
let symbolName: String
let description: String
var body: some View {
VStack(alignment: .leading) {
HStack(spacing: 5.0) {
Image(systemName: symbolName)
.foregroundColor(.accentColor)
.font(.callout)
Text(description)
.lineLimit(2)
.allowsTightening(/*@START_MENU_TOKEN@*/true/*@END_MENU_TOKEN@*/)
.fixedSize(horizontal: false, vertical: true)
.font(.caption)
}
Text(temperature)
.font(temperature.length < 4 ? .system(size: 72) : .system(size: 54) )
}
.frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140)
.padding()
.background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous))
}
}
struct HumidityCompactWidget: View {
let humidity: Int
let dewPoint: String?
var body: some View {
VStack(alignment: .leading) {
HStack(spacing: 5.0) {
Image(systemName: "humidity")
.foregroundColor(.accentColor)
.font(.callout)
Text("Humidity")
.textCase(.uppercase)
.font(.caption)
}
Text("\(humidity)%")
.font(.largeTitle)
.padding(.bottom, 5)
if let dewPoint {
Text("The dew point is \(dewPoint) right now.")
.lineLimit(3)
.allowsTightening(true)
.fixedSize(horizontal: false, vertical: true)
.font(.caption2)
}
}
.frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140)
.padding()
.background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous))
}
}
struct PressureCompactWidget: View {
let pressure: String
let unit: String
let low: Bool
var body: some View {
VStack(alignment: .leading) {
HStack(spacing: 5.0) {
Image(systemName: "gauge")
.foregroundColor(.accentColor)
.font(.callout)
Text("Pressure")
.textCase(.uppercase)
.font(.caption)
}
Text(pressure)
.font(pressure.length < 7 ? .system(size: 35) : .system(size: 30) )
Text(low ? "LOW" : "HIGH")
.padding(.bottom, 10)
Text(unit)
}
.frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140)
.padding()
.background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous))
}
}
struct WindCompactWidget: View {
let speed: String
let gust: String?
let direction: String?
var body: some View {
let hasGust = ((gust ?? "").isEmpty == false)
VStack(alignment: .leading) {
Label { Text("Wind").textCase(.uppercase) } icon: { Image(systemName: "wind").foregroundColor(.accentColor) }
if let direction {
Text("\(direction)")
.font(!hasGust ? .callout : .caption)
.padding(.bottom, 10)
}
Text(speed)
.font(!hasGust ? .system(size: 45) : .system(size: 35))
if let gust, !gust.isEmpty {
Text("Gusts \(gust)")
}
}
.frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140)
.padding()
.background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous))
}
}
/// Magnus Formula
func calculateDewPoint(temp: Float, relativeHumidity: Float) -> Double {
let a: Float = 17.27

View file

@ -1,233 +0,0 @@
//
// EnvironmentDefaultSeries.swift
// Meshtastic
//
// Created by Jake Bordens on 12/11/24.
//
import Charts
import Foundation
import SwiftUI
// This is the default configuration used by the EnvironmentMetricsLog view for the chart
extension MetricsSeriesList {
static var environmentDefaultChartSeries: MetricsSeriesList {
MetricsSeriesList([
// Temperature Series Configuration
MetricsChartSeries(
id: "temperature",
keyPath: \.temperature,
name: "Temperature",
abbreviatedName: "Temp",
conversion: { t in t.map { Float($0.localeTemperature()) } },
foregroundStyle: { chartRange in
let locale = NSLocale.current as NSLocale
let localeUnit = locale.object(forKey: NSLocale.Key(rawValue: "kCFLocaleTemperatureUnitKey"))
let format: UnitTemperature = localeUnit as? String ?? "Celsius" == "Fahrenheit" ? .fahrenheit : .celsius
let lowerBound = chartRange.map { Double($0.lowerBound) } ?? 0.0
let upperBound = chartRange.map { Double($0.upperBound) } ?? 100.0
let stops: [Gradient.Stop] = generateStops(minTemp: lowerBound, maxTemp: upperBound, tempUnit: format, opacity: 1.0)
return LinearGradient(stops: stops, startPoint: .bottom, endPoint: .top)
},
chartBody: { series, chartRange, time, temperature in
if let temperature {
AreaMark(
x: .value("Time", time),
yStart: .value(series.abbreviatedName, chartRange?.lowerBound.doubleValue ?? 0.0),
yEnd: .value(
series.abbreviatedName, temperature.localeTemperature())
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.alignsMarkStylesWithPlotArea()
.accessibilityHidden(true)
.opacity(0.6)
LineMark(
x: .value("Time", time),
y: .value(
series.abbreviatedName, temperature.localeTemperature())
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.alignsMarkStylesWithPlotArea()
}
}),
// Relative Humidity Series Configuration
MetricsChartSeries(
id: "relativeHumidity",
keyPath: \.relativeHumidity,
name: "Relative Humidity",
abbreviatedName: "Hum",
foregroundStyle: { _ in
.linearGradient(
colors: [Color(UIColor.purple.darker(componentDelta: 0.2)), .purple],
startPoint: .bottom, endPoint: .top
)
},
chartBody: { series, _, time, humidity in
if let humidity {
LineMark(
x: .value("Time", time),
y: .value(series.abbreviatedName, humidity)
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.alignsMarkStylesWithPlotArea()
}
}),
// Barometric Pressure Series Configuration
MetricsChartSeries(
id: "barometricPressure",
keyPath: \.barometricPressure,
name: "Barometric Pressure",
abbreviatedName: "Bar",
visible: false,
foregroundStyle: { _ in
.linearGradient(
colors: [Color(UIColor.green.darker(componentDelta: 0.3)), .green],
startPoint: .bottom, endPoint: .top
)
},
chartBody: { series, _, time, pressure in
if let pressure {
LineMark(
x: .value("Time", time),
y: .value(series.abbreviatedName, pressure)
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.alignsMarkStylesWithPlotArea()
}
}),
// Indoor Air Quality Series Configuration
MetricsChartSeries(
id: "iaq",
keyPath: \.iaq,
name: "Indoor Air Quality",
abbreviatedName: "IAQ",
visible: false,
foregroundStyle: { _ in .gray },
chartBody: { series, _, time, iaq in
if let iaq {
let iaqEnum = Iaq.getIaq(for: Int(iaq))
PointMark(
x: .value("Time", time),
y: .value(series.abbreviatedName, Float(iaq))
)
.symbol(Circle())
.foregroundStyle(iaqEnum.color)
LineMark(
x: .value("Time", time),
y: .value(series.abbreviatedName, Float(iaq))
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.alignsMarkStylesWithPlotArea()
}
}),
// Combined Wind Speed and Direction Series Configuration -- For use in Chart only
MetricsChartSeries(
id: "windSpeedAndDirection",
keyPath: \.windSpeedAndDirection,
name: "Wind Speed/Direction",
abbreviatedName: "Speed/Dir",
visible: false,
foregroundStyle: { _ in
.linearGradient(
colors: [Color(UIColor.yellow.darker(componentDelta: 0.3)), Color(UIColor.yellow.darker(componentDelta: 0.1))],
startPoint: .bottom, endPoint: .top
)
},
chartBody: { series, _, time, wsad in
if let wsad {
// debug data: var wsad = WindSpeedAndDirection(windSpeed:Float.random(in:0...25), windDirection: Int32.random(in:0..<3)*90 )
LineMark(
x: .value("Time", time),
y: .value(series.abbreviatedName, wsad.windSpeed)
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.alignsMarkStylesWithPlotArea()
PointMark(
x: .value("Time", time),
y: .value(series.abbreviatedName, wsad.windSpeed)
)
.symbol {
if let wd = wsad.windDirection {
Image(systemName: "location.north.circle.fill")
.symbolRenderingMode(.palette)
.foregroundStyle(Color.white, Color(UIColor.yellow.darker(componentDelta: 0.3)))
.rotationEffect(
.degrees(Double(wd)))
}
}.foregroundStyle(.yellow)
}
})
])
}
}
// Extension to combine windspeed and direction into one attribute for rendering
// for rendering on the chart.
@objc class WindSpeedAndDirection: NSObject, Plottable, Comparable {
let windSpeed: Float
let windDirection: Int32?
init(windSpeed: Float, windDirection: Int32?) {
self.windSpeed = windSpeed
self.windDirection = windDirection
}
// Plottable Conformance
required init?(primitivePlottable: Float) { nil }
var primitivePlottable: Float { windSpeed }
static func < (lhs: WindSpeedAndDirection, rhs: WindSpeedAndDirection) -> Bool {
lhs.windSpeed < rhs.windSpeed
}
}
@objc extension TelemetryEntity {
var windSpeedAndDirection: WindSpeedAndDirection? {
guard let windSpeed = self.windSpeed else { return nil }
return WindSpeedAndDirection(windSpeed: windSpeed, windDirection: self.windDirection)
}
}
// From: https://github.com/meshtastic/Meshtastic-Apple/pull/1013/commits/bc932567c742c8fa9fd30752237b10cb762c5ef3
// Set up gradient stops relative to the scale of the temperature chart
func generateStops(minTemp: Double, maxTemp: Double, tempUnit: UnitTemperature, opacity: Double) -> [Gradient.Stop] {
var gradientStops = [Gradient.Stop]()
let stopTargets: [(Double, Color)] = [
((tempUnit == .celsius ? 0 : 32), .blue),
((tempUnit == .celsius ? 20 : 68), .yellow),
((tempUnit == .celsius ? 30 : 86), .orange),
((tempUnit == .celsius ? 55 : 125), .red)
]
for (stopValue, color) in stopTargets {
let stopLocation = transform(stopValue, from: minTemp...maxTemp, to: 0...1)
gradientStops.append(Gradient.Stop(color: color.opacity(opacity), location: stopLocation))
}
return gradientStops
}
// Map inputRange to outputRange
func transform<T: FloatingPoint>(_ input: T, from inputRange: ClosedRange<T>, to outputRange: ClosedRange<T>) -> T {
// need to determine what that value would be in (to.low, to.high)
// difference in output range / difference in input range = slope
let slope = (outputRange.upperBound - outputRange.lowerBound) / (inputRange.upperBound - inputRange.lowerBound)
// slope * normalized input + output lower
let output = slope * (input - inputRange.lowerBound) + outputRange.lowerBound
return output
}

View file

@ -73,6 +73,77 @@ extension MetricsColumnList {
}
}),
// Various Lux
MetricsTableColumn(
id: "lux",
keyPath: \.lux,
name: "Lux",
abbreviatedName: "Lux",
minWidth: 30, maxWidth: 50,
visible: false,
tableBody: { _, lux in
lux.map {
Text("\($0.formatted(.number.grouping(.never).precision(.fractionLength(1))))")
} ?? Text(Constants.nilValueIndicator)
}),
MetricsTableColumn(
id: "whiteLux",
keyPath: \.whiteLux,
name: "White Lux",
abbreviatedName: "White",
minWidth: 30, maxWidth: 50,
visible: false,
tableBody: { _, lux in
lux.map {
Text("\($0.formatted(.number.grouping(.never).precision(.fractionLength(1))))")
} ?? Text(Constants.nilValueIndicator)
}),
MetricsTableColumn(
id: "uvLux",
keyPath: \.uvLux,
name: "UV Lux",
abbreviatedName: "UV",
minWidth: 30, maxWidth: 50,
visible: false,
tableBody: { _, lux in
lux.map {
Text("\($0.formatted(.number.grouping(.never).precision(.fractionLength(1))))")
} ?? Text(Constants.nilValueIndicator)
}),
MetricsTableColumn(
id: "irLux",
keyPath: \.irLux,
name: "IR Lux",
abbreviatedName: "IR",
minWidth: 30, maxWidth: 50,
visible: false,
tableBody: { _, lux in
lux.map {
Text("\($0.formatted(.number.grouping(.never).precision(.fractionLength(1))))")
} ?? Text(Constants.nilValueIndicator)
}),
// Radiation
MetricsTableColumn(
id: "radiation",
keyPath: \.radiation,
name: "Radiation",
abbreviatedName: "☢️",
minWidth: 30, maxWidth: 50,
visible: false,
tableBody: { _, radiation in
radiation.map {
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
Text(verbatim: "\($0.formatted(.number.grouping(.never).precision(.fractionLength(1)))) µR/h")
} else {
Text("\($0.formatted(.number.grouping(.never).precision(.fractionLength(1))))")
}
} ?? Text(Constants.nilValueIndicator)
}),
// Wind Direction Series Configuration
MetricsTableColumn(
id: "windDirection",
@ -127,6 +198,126 @@ extension MetricsColumnList {
} ?? Text(verbatim: Constants.nilValueIndicator)
}),
// Rainfall 1-hour
MetricsTableColumn(
id: "rainfall1H",
keyPath: \.rainfall1H,
name: "Rainfall (1H)",
abbreviatedName: "Rain 1H",
minWidth: 30, maxWidth: 60,
visible: false,
tableBody: { _, rainfall in
rainfall.map {
let rain = Measurement(
value: Double($0), unit: UnitLength.millimeters)
return Text(
rain.formatted(
.measurement(
width: .abbreviated,
numberFormatStyle: .number.grouping(.never)
.precision(
.fractionLength(0))))
)
} ?? Text(Constants.nilValueIndicator)
}),
// Rainfall 24-hour
MetricsTableColumn(
id: "rainfall24H",
keyPath: \.rainfall24H,
name: "Rainfall (24H)",
abbreviatedName: "Rain 24H",
minWidth: 30, maxWidth: 60,
visible: false,
tableBody: { _, rainfall in
rainfall.map {
let rain = Measurement(
value: Double($0), unit: UnitLength.millimeters)
return Text(
rain.formatted(
.measurement(
width: .abbreviated,
numberFormatStyle: .number.grouping(.never)
.precision(
.fractionLength(0))))
)
} ?? Text(Constants.nilValueIndicator)
}),
// Weight
MetricsTableColumn(
id: "weight",
keyPath: \.weight,
name: "Weight",
abbreviatedName: "kg",
minWidth: 30, maxWidth: 60,
visible: false,
tableBody: { _, weight in
weight.map {
let weight = Measurement(
value: Double($0), unit: UnitMass.kilograms)
return Text(
weight.formatted(
.measurement(
width: .abbreviated,
numberFormatStyle: .number.grouping(.never)
.precision(
.fractionLength(0))))
)
} ?? Text(Constants.nilValueIndicator)
}),
// Distance sensor, often used for water level
MetricsTableColumn(
id: "distance",
keyPath: \.distance,
name: "Distance",
abbreviatedName: "Dist",
minWidth: 30, maxWidth: 50,
visible: false,
tableBody: { _, distance in
distance.map {
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
Text(verbatim: "\($0.formatted(.number.grouping(.never).precision(.fractionLength(1)))) mm")
} else {
Text("\($0.formatted(.number.grouping(.never).precision(.fractionLength(1))))")
}
} ?? Text(Constants.nilValueIndicator)
}),
// Soil Temperature
MetricsTableColumn(
id: "soilTemperature",
keyPath: \.soilTemperature,
name: "Soil Temperature",
abbreviatedName: "Soil Temp",
minWidth: 30, maxWidth: 50,
visible: false,
tableBody: { _, soilTemperature in
soilTemperature.map {
Text($0.formattedTemperature())
} ?? Text(verbatim: Constants.nilValueIndicator)
}),
// Soil Moisture
MetricsTableColumn(
id: "soilMoisture",
keyPath: \.soilMoisture,
name: "Soil Moisture",
abbreviatedName: "Moist",
minWidth: 30, maxWidth: 50,
visible: false,
tableBody: { _, moisture in
moisture.map {
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
Text("\($0.formatted(.number.grouping(.never).precision(.fractionLength(0))))%")
} else {
Text("\($0.formatted(.number.grouping(.never).precision(.fractionLength(0))))")
}
} ?? Text(Constants.nilValueIndicator)
}),
// Timestamp Series Configuration -- for use in table only
MetricsTableColumn(
id: "time",

View file

@ -0,0 +1,522 @@
//
// EnvironmentDefaultSeries.swift
// Meshtastic
//
// Created by Jake Bordens on 12/11/24.
//
import Charts
import Foundation
import SwiftUI
// This is the default configuration used by the EnvironmentMetricsLog view for the chart
extension MetricsSeriesList {
static var environmentDefaultChartSeries: MetricsSeriesList {
MetricsSeriesList([
// Temperature Series Configuration
MetricsChartSeries(
id: "temperature",
keyPath: \.temperature,
name: "Temperature",
abbreviatedName: "Temp",
minumumYAxisSpan: 50.0,
conversion: { t in t.map { Float($0.localeTemperature()) } },
foregroundStyle: { chartRange in
let locale = NSLocale.current as NSLocale
let localeUnit = locale.object(forKey: NSLocale.Key(rawValue: "kCFLocaleTemperatureUnitKey"))
let format: UnitTemperature = localeUnit as? String ?? "Celsius" == "Fahrenheit" ? .fahrenheit : .celsius
let lowerBound = chartRange.map { Double($0.lowerBound) } ?? 0.0
let upperBound = chartRange.map { Double($0.upperBound) } ?? 100.0
let stops: [Gradient.Stop] = generateStops(minTemp: lowerBound, maxTemp: upperBound, tempUnit: format, opacity: 1.0)
return LinearGradient(stops: stops, startPoint: .bottom, endPoint: .top)
},
chartBody: { series, chartRange, time, temperature in
if let temperature {
AreaMark(
x: .value("Time", time),
yStart: .value(series.abbreviatedName, chartRange?.lowerBound.doubleValue ?? 0.0),
yEnd: .value(
series.abbreviatedName, temperature.localeTemperature())
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.alignsMarkStylesWithPlotArea()
.accessibilityHidden(true)
.opacity(0.6)
LineMark(
x: .value("Time", time),
y: .value(
series.abbreviatedName, temperature.localeTemperature())
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.alignsMarkStylesWithPlotArea()
}
}),
// Relative Humidity Series Configuration
MetricsChartSeries(
id: "relativeHumidity",
keyPath: \.relativeHumidity,
name: "Relative Humidity",
abbreviatedName: "Hum",
initialYAxisRange: 0.0...100.0,
foregroundStyle: { _ in
.linearGradient(
colors: [Color(UIColor.purple.darker(componentDelta: 0.2)), .purple],
startPoint: .bottom, endPoint: .top
)
},
chartBody: { series, _, time, humidity in
if let humidity {
LineMark(
x: .value("Time", time),
y: .value(series.abbreviatedName, humidity)
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.alignsMarkStylesWithPlotArea()
}
}),
// Barometric Pressure Series Configuration
MetricsChartSeries(
id: "barometricPressure",
keyPath: \.barometricPressure,
name: "Barometric Pressure",
abbreviatedName: "Bar",
visible: false,
foregroundStyle: { _ in
.linearGradient(
colors: [Color(UIColor.green.darker(componentDelta: 0.3)), .green],
startPoint: .bottom, endPoint: .top
)
},
chartBody: { series, _, time, pressure in
if let pressure {
LineMark(
x: .value("Time", time),
y: .value(series.abbreviatedName, pressure)
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.alignsMarkStylesWithPlotArea()
}
}),
// Indoor Air Quality Series Configuration
MetricsChartSeries(
id: "iaq",
keyPath: \.iaq,
name: "Indoor Air Quality",
abbreviatedName: "IAQ",
visible: false,
foregroundStyle: { _ in .gray },
chartBody: { series, _, time, iaq in
if let iaq {
let iaqEnum = Iaq.getIaq(for: Int(iaq))
PointMark(
x: .value("Time", time),
y: .value(series.abbreviatedName, Float(iaq))
)
.symbol(Circle())
.foregroundStyle(iaqEnum.color)
LineMark(
x: .value("Time", time),
y: .value(series.abbreviatedName, Float(iaq))
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.alignsMarkStylesWithPlotArea()
}
}),
// Lux
MetricsChartSeries(
id: "lux",
keyPath: \.lux,
name: "Lux",
abbreviatedName: "Lux",
visible: false,
foregroundStyle: { _ in
.linearGradient(
colors: [Color(UIColor.cyan.lighter(componentDelta: 0.3)), .cyan],
startPoint: .bottom, endPoint: .top
)
},
chartBody: { series, _, time, lux in
if let lux {
LineMark(
x: .value("Time", time),
y: .value(series.abbreviatedName, lux)
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.alignsMarkStylesWithPlotArea()
}
}),
// White Lux
MetricsChartSeries(
id: "whiteLux",
keyPath: \.whiteLux,
name: "White Lux",
abbreviatedName: "White",
visible: false,
foregroundStyle: { _ in
.linearGradient(
colors: [Color(UIColor.cyan.lighter(componentDelta: 0.5)), Color(UIColor.cyan.lighter(componentDelta: 0.2))],
startPoint: .bottom, endPoint: .top
)
},
chartBody: { series, _, time, lux in
if let lux {
LineMark(
x: .value("Time", time),
y: .value(series.abbreviatedName, lux)
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.alignsMarkStylesWithPlotArea()
}
}),
// UV Lux
MetricsChartSeries(
id: "uvLux",
keyPath: \.uvLux,
name: "UV Lux",
abbreviatedName: "UV",
visible: false,
foregroundStyle: { _ in
.linearGradient(
colors: [Color(UIColor.systemIndigo.lighter(componentDelta: 0.4)), Color(UIColor.systemIndigo.lighter(componentDelta: 0.2))],
startPoint: .bottom, endPoint: .top
)
},
chartBody: { series, _, time, lux in
if let lux {
LineMark(
x: .value("Time", time),
y: .value(series.abbreviatedName, lux)
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.alignsMarkStylesWithPlotArea()
}
}),
// IR Lux
MetricsChartSeries(
id: "irLux",
keyPath: \.irLux,
name: "IR Lux",
abbreviatedName: "IR",
visible: false,
foregroundStyle: { _ in
.linearGradient(
colors: [Color(UIColor.red.darker(componentDelta: 0.5)), .red],
startPoint: .bottom, endPoint: .top
)
},
chartBody: { series, _, time, lux in
if let lux {
LineMark(
x: .value("Time", time),
y: .value(series.abbreviatedName, lux)
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.alignsMarkStylesWithPlotArea()
}
}),
// Radiation
MetricsChartSeries(
id: "radiation",
keyPath: \.radiation,
name: "Radiation",
abbreviatedName: "☢️",
minumumYAxisSpan: 20.0,
visible: false,
foregroundStyle: { _ in
.linearGradient(
colors: [Color(UIColor.orange.darker(componentDelta: 0.4)), .orange],
startPoint: .bottom, endPoint: .top
)
},
chartBody: { series, _, time, radiation in
if let radiation {
LineMark(
x: .value("Time", time),
y: .value(series.abbreviatedName, radiation)
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.alignsMarkStylesWithPlotArea()
}
}),
// Combined Wind Speed and Direction Series Configuration -- For use in Chart only
MetricsChartSeries(
id: "windSpeedAndDirection",
keyPath: \.windSpeedAndDirection,
name: "Wind Speed/Direction",
abbreviatedName: "Speed/Dir",
visible: false,
foregroundStyle: { _ in
.linearGradient(
colors: [Color(UIColor.yellow.darker(componentDelta: 0.3)), Color(UIColor.yellow.darker(componentDelta: 0.1))],
startPoint: .bottom, endPoint: .top
)
},
chartBody: { series, _, time, wsad in
if let wsad {
// debug data: var wsad = WindSpeedAndDirection(windSpeed:Float.random(in:0...25), windDirection: Int32.random(in:0..<3)*90 )
LineMark(
x: .value("Time", time),
y: .value(series.abbreviatedName, wsad.windSpeed)
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.alignsMarkStylesWithPlotArea()
PointMark(
x: .value("Time", time),
y: .value(series.abbreviatedName, wsad.windSpeed)
)
.symbol {
if let wd = wsad.windDirection {
Image(systemName: "location.north.circle.fill")
.symbolRenderingMode(.palette)
.foregroundStyle(Color.white, Color(UIColor.yellow.darker(componentDelta: 0.3)))
.rotationEffect(
.degrees(Double(wd)))
}
}.foregroundStyle(.yellow)
}
}),
// Rainfaill 1-hour
MetricsChartSeries(
id: "rainfall1H",
keyPath: \.rainfall1H,
name: "Rainfall 1H",
abbreviatedName: "Rain 1H",
visible: false,
foregroundStyle: { _ in
.linearGradient(
colors: [Color(UIColor.systemBlue.darker(componentDelta: 0.5)), .blue],
startPoint: .bottom, endPoint: .top
)
},
chartBody: { series, _, time, rainfall in
if let rainfall {
LineMark(
x: .value("Time", time),
y: .value(series.abbreviatedName, rainfall)
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.alignsMarkStylesWithPlotArea()
}
}),
// Rainfaill 24-hour
MetricsChartSeries(
id: "rainfall24H",
keyPath: \.rainfall24H,
name: "Rainfall 24H",
abbreviatedName: "Rain 24H",
visible: false,
foregroundStyle: { _ in
.linearGradient(
colors: [Color(UIColor.systemBlue.darker(componentDelta: 0.5)), .cyan],
startPoint: .bottom, endPoint: .top
)
},
chartBody: { series, _, time, rainfall in
if let rainfall {
LineMark(
x: .value("Time", time),
y: .value(series.abbreviatedName, rainfall)
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.alignsMarkStylesWithPlotArea()
}
}),
// Weight
MetricsChartSeries(
id: "weight",
keyPath: \.weight,
name: "Weight",
abbreviatedName: "kg",
visible: false,
foregroundStyle: { _ in
.linearGradient(
colors: [Color(UIColor.systemPink.darker(componentDelta: 0.5)), .pink],
startPoint: .bottom, endPoint: .top
)
},
chartBody: { series, _, time, weight in
if let weight {
LineMark(
x: .value("Time", time),
y: .value(series.abbreviatedName, weight)
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.alignsMarkStylesWithPlotArea()
}
}),
// Distance
MetricsChartSeries(
id: "distance",
keyPath: \.distance,
name: "Distance",
abbreviatedName: "Dist",
visible: false,
foregroundStyle: { _ in
.linearGradient(
colors: [Color(UIColor.systemTeal.darker(componentDelta: 0.7)), Color(UIColor.systemTeal)],
startPoint: .bottom, endPoint: .top
)
},
chartBody: { series, _, time, distance in
if let distance {
LineMark(
x: .value("Time", time),
y: .value(series.abbreviatedName, distance)
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.alignsMarkStylesWithPlotArea()
}
}),
// Soil Temperature
MetricsChartSeries(
id: "soilTemperature",
keyPath: \.soilTemperature,
name: "Soil Temperature",
abbreviatedName: "Soil Temp",
visible: false,
foregroundStyle: { _ in
.linearGradient(
colors: [Color(UIColor.brown.darker(componentDelta: 0.4)), .brown],
startPoint: .bottom, endPoint: .top
)
},
chartBody: { series, _, time, soilTemp in
if let soilTemp {
LineMark(
x: .value("Time", time),
y: .value(series.abbreviatedName, soilTemp)
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.alignsMarkStylesWithPlotArea()
}
}),
// Soil Temperature
MetricsChartSeries(
id: "soilMoisture",
keyPath: \.soilMoisture,
name: "Soil Moisture",
abbreviatedName: "Moist",
visible: false,
foregroundStyle: { _ in
.linearGradient(
colors: [Color(UIColor.blue.darker(componentDelta: 0.4)), .brown],
startPoint: .bottom, endPoint: .top
)
},
chartBody: { series, _, time, soilMoisture in
if let soilMoisture {
LineMark(
x: .value("Time", time),
y: .value(series.abbreviatedName, soilMoisture)
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.alignsMarkStylesWithPlotArea()
}
}),
])
}
}
// Extension to combine windspeed and direction into one attribute for rendering
// for rendering on the chart.
@objc class WindSpeedAndDirection: NSObject, Plottable, Comparable {
let windSpeed: Float
let windDirection: Int32?
init(windSpeed: Float, windDirection: Int32?) {
self.windSpeed = windSpeed
self.windDirection = windDirection
}
// Plottable Conformance
required init?(primitivePlottable: Float) { nil }
var primitivePlottable: Float { windSpeed }
static func < (lhs: WindSpeedAndDirection, rhs: WindSpeedAndDirection) -> Bool {
lhs.windSpeed < rhs.windSpeed
}
}
@objc extension TelemetryEntity {
var windSpeedAndDirection: WindSpeedAndDirection? {
guard let windSpeed = self.windSpeed else { return nil }
return WindSpeedAndDirection(windSpeed: windSpeed, windDirection: self.windDirection)
}
}
// From: https://github.com/meshtastic/Meshtastic-Apple/pull/1013/commits/bc932567c742c8fa9fd30752237b10cb762c5ef3
// Set up gradient stops relative to the scale of the temperature chart
func generateStops(minTemp: Double, maxTemp: Double, tempUnit: UnitTemperature, opacity: Double) -> [Gradient.Stop] {
var gradientStops = [Gradient.Stop]()
let stopTargets: [(Double, Color)] = [
((tempUnit == .celsius ? 0 : 32), .blue),
((tempUnit == .celsius ? 20 : 68), .yellow),
((tempUnit == .celsius ? 30 : 86), .orange),
((tempUnit == .celsius ? 55 : 125), .red)
]
for (stopValue, color) in stopTargets {
let stopLocation = transform(stopValue, from: minTemp...maxTemp, to: 0...1)
gradientStops.append(Gradient.Stop(color: color.opacity(opacity), location: stopLocation))
}
return gradientStops
}
// Map inputRange to outputRange
func transform<T: FloatingPoint>(_ input: T, from inputRange: ClosedRange<T>, to outputRange: ClosedRange<T>) -> T {
// need to determine what that value would be in (to.low, to.high)
// difference in output range / difference in input range = slope
let slope = (outputRange.upperBound - outputRange.lowerBound) / (inputRange.upperBound - inputRange.lowerBound)
// slope * normalized input + output lower
let output = slope * (input - inputRange.lowerBound) + outputRange.lowerBound
return output
}

View file

@ -163,7 +163,7 @@ struct NodeDetail: View {
}
}
if let firstHeard = node.firstHeard, firstHeard.timeIntervalSince1970 > 0 {
if let firstHeard = node.firstHeard, firstHeard.timeIntervalSince1970 > 0 && firstHeard < Calendar.current.date(byAdding: .year, value: 1, to: Date())! {
HStack {
Label {
Text("First heard")
@ -184,7 +184,7 @@ struct NodeDetail: View {
}
}
if let lastHeard = node.lastHeard, lastHeard.timeIntervalSince1970 > 0 {
if let lastHeard = node.lastHeard, lastHeard.timeIntervalSince1970 > 0 && lastHeard < Calendar.current.date(byAdding: .year, value: 1, to: Date())! {
HStack {
Label {
Text("Last heard")
@ -195,8 +195,10 @@ struct NodeDetail: View {
Spacer()
if dateFormatRelative, let text = Self.relativeFormatter.string(for: lastHeard) {
Text(text)
.textSelection(.enabled)
if lastHeard.formatted() != "unknown.age".localized {
Text(text)
.textSelection(.enabled)
}
} else {
Text(lastHeard.formatted())
.textSelection(.enabled)
@ -212,7 +214,7 @@ struct NodeDetail: View {
// to use with WeatherKit, or has actual data in the most recent EnvironmentMetrics entity
// that will be rendered in this section.
if node.hasPositions && UserDefaults.environmentEnableWeatherKit
|| node.hasDataForLatestEnvironmentMetrics(attributes: ["iaq", "temperature", "relativeHumidity", "barometricPressure", "windSpeed"]) {
|| node.hasDataForLatestEnvironmentMetrics(attributes: ["iaq", "temperature", "relativeHumidity", "barometricPressure", "windSpeed", "radiation", "weight", "distance", "soilTemperature", "soilMoisture"]) {
Section("Environment") {
if !node.hasEnvironmentMetrics {
LocalWeatherConditions(location: node.latestPosition?.nodeLocation)
@ -245,6 +247,44 @@ struct NodeDetail: View {
WindCompactWidget(speed: windSpeedMeasurement.formatted(.measurement(width: .abbreviated, numberFormatStyle: .number.precision(.fractionLength(0)))),
gust: node.latestEnvironmentMetrics?.windGust ?? 0.0 > 0.0 ? windGust?.formatted(.measurement(width: .abbreviated, numberFormatStyle: .number.precision(.fractionLength(0)))) : "", direction: direction)
}
if let rainfall1h = node.latestEnvironmentMetrics?.rainfall1H {
let locale = NSLocale.current as NSLocale
let usesMetricSystem = locale.usesMetricSystem // Returns true for metric (mm), false for imperial (inches)
let unit = usesMetricSystem ? UnitLength.millimeters : UnitLength.inches
let unitLabel = usesMetricSystem ? "mm" : "in"
let measurement = Measurement(value: Double(rainfall1h), unit: UnitLength.millimeters)
let decimals = usesMetricSystem ? 0 : 1
let formattedRain = measurement.converted(to: unit).value.formatted(.number.precision(.fractionLength(decimals)))
RainfallCompactWidget(timespan: .rainfall1H, rainfall: formattedRain, unit: unitLabel)
}
if let rainfall24h = node.latestEnvironmentMetrics?.rainfall24H {
let locale = NSLocale.current as NSLocale
let usesMetricSystem = locale.usesMetricSystem // Returns true for metric (mm), false for imperial (inches)
let unit = usesMetricSystem ? UnitLength.millimeters : UnitLength.inches
let unitLabel = usesMetricSystem ? "mm" : "in"
let measurement = Measurement(value: Double(rainfall24h), unit: UnitLength.millimeters)
let decimals = usesMetricSystem ? 0 : 1
let formattedRain = measurement.converted(to: unit).value.formatted(.number.precision(.fractionLength(decimals)))
RainfallCompactWidget(timespan: .rainfall24H, rainfall: formattedRain, unit: unitLabel)
}
if let radiation = node.latestEnvironmentMetrics?.radiation {
RadiationCompactWidget(radiation: radiation.formatted(.number.precision(.fractionLength(1))), unit: "µR/hr")
}
if let weight = node.latestEnvironmentMetrics?.weight {
WeightCompactWidget(weight: weight.formatted(.number.precision(.fractionLength(1))), unit: "kg")
}
if let distance = node.latestEnvironmentMetrics?.distance {
DistanceCompactWidget(distance: distance.formatted(.number.precision(.fractionLength(0))), unit: "mm")
}
if let soilTemperature = node.latestEnvironmentMetrics?.soilTemperature {
let locale = NSLocale.current as NSLocale
let localeUnit = locale.object(forKey: NSLocale.Key(rawValue: "kCFLocaleTemperatureUnitKey"))
let unit = localeUnit as? String ?? "Celsius" == "Fahrenheit" ? "°F" : "°C"
SoilTemperatureCompactWidget(temperature: soilTemperature.localeTemperature().formatted(.number.precision(.fractionLength(0))), unit: unit)
}
if let soilMoisture = node.latestEnvironmentMetrics?.soilMoisture {
SoilMoistureCompactWidget(moisture: soilMoisture.formatted(.number.precision(.fractionLength(0))), unit: "%")
}
}
.padding(node.latestEnvironmentMetrics?.iaq ?? -1 > 0 ? .bottom : .vertical)
}

View file

@ -64,7 +64,7 @@ struct NodeListItem: View {
let (image, color) = userKeyStatus
IconAndText(systemName: image,
imageColor: color,
text: node.user?.longName ?? "unknown".localized,
text: node.user?.longName?.addingVariationSelectors ?? "unknown".localized,
textColor: .primary)
if node.favorite {
Spacer()
@ -77,10 +77,10 @@ struct NodeListItem: View {
imageColor: .green,
text: "connected".localized)
}
if node.lastHeard?.timeIntervalSince1970 ?? 0 > 0 {
if node.lastHeard?.timeIntervalSince1970 ?? 0 > 0 && node.lastHeard! < Calendar.current.date(byAdding: .year, value: 1, to: Date())!{
IconAndText(systemName: node.isOnline ? "checkmark.circle.fill" : "moon.circle.fill",
imageColor: node.isOnline ? .green : .orange,
text: node.lastHeard?.formatted() ?? "unknown")
text: node.lastHeard?.formatted() ?? "unknown.age".localized)
}
let role = DeviceRoles(rawValue: Int(node.user?.role ?? 0))
IconAndText(systemName: role?.systemName ?? "figure",

View file

@ -264,7 +264,7 @@ struct NodeList: View {
columnVisibility: columnVisibility
)
.edgesIgnoringSafeArea([.leading, .trailing])
.navigationBarTitle(String(node.user?.longName ?? "unknown".localized), displayMode: .inline)
.navigationBarTitle(String(node.user?.longName?.addingVariationSelectors ?? "unknown".localized), displayMode: .inline)
.navigationBarItems(
trailing: ZStack {
if UIDevice.current.userInterfaceIdiom != .phone {

View file

@ -25,34 +25,6 @@ struct AppData: View {
GPSStatus()
}
Divider()
Button(action: {
let container = NSPersistentContainer(name: "Meshtastic")
guard let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
Logger.data.error("nil File path for back")
return
}
do {
try container.copyPersistentStores(to: url.appendingPathComponent("backup").appendingPathComponent("\(UserDefaults.preferredPeripheralNum)"), overwriting: true)
loadFiles()
Logger.data.notice("🗂️ Made a core data backup to backup/\(UserDefaults.preferredPeripheralNum)")
} catch {
Logger.data.error("🗂️ Core data backup copy error: \(error, privacy: .public)")
}
}) {
Label {
Text("Backup Database")
.font(idiom == .phone ? .callout : .title)
} icon: {
Image(systemName: "cylinder.split.1x2")
.symbolRenderingMode(.hierarchical)
.font(idiom == .phone ? .callout : .title)
.frame(width: 35)
}
}
.buttonStyle(.bordered)
.buttonBorderShape(.capsule)
.controlSize(.large)
Divider()
}
List(files, id: \.self) { file in
@ -62,20 +34,6 @@ struct AppData: View {
Label {
Text("Node Core Data Backup \(file.pathComponents[(idiom == .phone || idiom == .pad) ? 9 : 10])/\(file.lastPathComponent) - \(file.creationDate?.formatted() ?? "") - \(file.fileSizeString)")
.swipeActions {
Button(role: .none) {
bleManager.disconnectPeripheral(reconnect: false)
let container = NSPersistentContainer(name: "Meshtastic")
do {
try container.restorePersistentStore(from: file.absoluteURL)
UserDefaults.preferredPeripheralId = ""
UserDefaults.preferredPeripheralNum = Int(file.pathComponents[(idiom == .phone || idiom == .pad) ? 9 : 10]) ?? 0
Logger.data.notice("🗂️ Restored a core data backup to backup/\(UserDefaults.preferredPeripheralNum, privacy: .public)")
} catch {
Logger.data.error("🗂️ Core data restore copy error: \(error, privacy: .public)")
}
} label: {
Label("restore", systemImage: "arrow.counterclockwise")
}
Button(role: .destructive) {
do {
try FileManager.default.removeItem(at: file)

View file

@ -21,6 +21,7 @@ struct DeviceConfig: View {
@State private var isPresentingFactoryResetConfirm = false
@State var hasChanges = false
@State var deviceRole = 0
@State private var pendingDeviceRole = 0
@State var buzzerGPIO = 0
@State var buttonGPIO = 0
@State var rebroadcastMode = 0
@ -29,6 +30,10 @@ struct DeviceConfig: View {
@State var ledHeartbeatEnabled = true
@State var tripleClickAsAdHocPing = true
@State var tzdef = ""
@State private var showRouterWarning = false
@State private var confirmWarning = false
var body: some View {
VStack {
@ -39,9 +44,37 @@ struct DeviceConfig: View {
VStack(alignment: .leading) {
Picker("Device Role", selection: $deviceRole ) {
ForEach(DeviceRoles.allCases) { dr in
Text(dr.name)
Text(dr.name).tag(dr.rawValue as Int)
}
}
.onChange(of: deviceRole) { oldValue, newValue in
if !confirmWarning {
if [2, 11].contains(newValue) {
pendingDeviceRole = newValue
deviceRole = oldValue // Reset selection until confirmed
showRouterWarning = true
}
} else {
confirmWarning = false
}
}
.confirmationDialog(
"Are you sure?",
isPresented: $showRouterWarning,
titleVisibility: .visible
) {
Button("Confirm") {
deviceRole = pendingDeviceRole
pendingDeviceRole = 0
confirmWarning = true
}
Button("Cancel", role: .cancel) {
pendingDeviceRole = 0
}
} message: {
Text("The Router roles are designed for high vantage locations like mountaintops and towers. This node needs to be able to have a good direct connection to most of the nodes on the network or else this will significantly hurt the network.")
}
Text(DeviceRoles(rawValue: deviceRole)?.description ?? "")
.foregroundColor(.gray)
.font(.callout)

View file

@ -230,31 +230,88 @@ struct MQTTConfig: View {
}
.scrollDismissesKeyboard(.interactively)
.disabled(self.bleManager.connectedPeripheral == nil || node?.mqttConfig == nil)
}
SaveConfigButton(node: node, hasChanges: $hasChanges) {
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? -1, context: context)
if connectedNode != nil {
var mqtt = ModuleConfig.MQTTConfig()
mqtt.enabled = self.enabled
mqtt.proxyToClientEnabled = self.proxyToClientEnabled
mqtt.address = self.address
mqtt.username = self.username
mqtt.password = self.password
mqtt.root = self.root
mqtt.encryptionEnabled = self.encryptionEnabled
mqtt.jsonEnabled = self.jsonEnabled
mqtt.tlsEnabled = self.tlsEnabled
mqtt.mapReportingEnabled = self.mapReportingEnabled
mqtt.mapReportSettings.positionPrecision = UInt32(self.mapPositionPrecision)
mqtt.mapReportSettings.publishIntervalSecs = UInt32(self.mapPublishIntervalSecs)
let adminMessageId = bleManager.saveMQTTConfig(config: mqtt, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
if adminMessageId > 0 {
// Should show a saved successfully alert once I know that to be true
// for now just disable the button after a successful save
hasChanges = false
goBack()
SaveConfigButton(node: node, hasChanges: $hasChanges) {
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? -1, context: context)
if connectedNode != nil {
var mqtt = ModuleConfig.MQTTConfig()
mqtt.enabled = self.enabled
mqtt.proxyToClientEnabled = self.proxyToClientEnabled
mqtt.address = self.address
mqtt.username = self.username
mqtt.password = self.password
mqtt.root = self.root
mqtt.encryptionEnabled = self.encryptionEnabled
mqtt.jsonEnabled = self.jsonEnabled
mqtt.tlsEnabled = self.tlsEnabled
mqtt.mapReportingEnabled = self.mapReportingEnabled
mqtt.mapReportSettings.positionPrecision = UInt32(self.mapPositionPrecision)
mqtt.mapReportSettings.publishIntervalSecs = UInt32(self.mapPublishIntervalSecs)
let adminMessageId = bleManager.saveMQTTConfig(config: mqtt, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
if adminMessageId > 0 {
// Should show a saved successfully alert once I know that to be true
// for now just disable the button after a successful save
hasChanges = false
goBack()
}
}
}.onChange(of: enabled) { _, newEnabled in
if newEnabled != node?.mqttConfig?.enabled { hasChanges = true }
}
.onChange(of: proxyToClientEnabled) { _, newProxyToClientEnabled in
if newProxyToClientEnabled {
jsonEnabled = false
tlsEnabled = false
}
if newProxyToClientEnabled != node?.mqttConfig?.proxyToClientEnabled { hasChanges = true }
}
.onChange(of: address) { _, newAddress in
if newAddress != node?.mqttConfig?.address ?? "" { hasChanges = true }
}
.onChange(of: username) { _, newUsername in
if newUsername != node?.mqttConfig?.username ?? "" { hasChanges = true }
}
.onChange(of: password) { _, newPassword in
if newPassword != node?.mqttConfig?.password ?? "" { hasChanges = true }
}
.onChange(of: root) { _, newRoot in
if newRoot != node?.mqttConfig?.root ?? "" { hasChanges = true }
}
.onChange(of: selectedTopic) { _, newSelectedTopic in
root = newSelectedTopic
}
.onChange(of: encryptionEnabled) { _, newEncryptionEnabled in
if newEncryptionEnabled != node?.mqttConfig?.encryptionEnabled { hasChanges = true }
}
.onChange(of: jsonEnabled) { _, newJsonEnabled in
if newJsonEnabled {
proxyToClientEnabled = false
}
if newJsonEnabled != node?.mqttConfig?.jsonEnabled { hasChanges = true }
}
.onChange(of: tlsEnabled) { _, newTlsEnabled in
if address.lowercased() == "mqtt.meshtastic.org" {
tlsEnabled = false
} else {
if newTlsEnabled != node?.mqttConfig?.tlsEnabled { hasChanges = true }
}
}
.onChange(of: mqttConnected) { _, newMqttConnected in
if newMqttConnected == false {
if bleManager.mqttProxyConnected {
bleManager.mqttManager.disconnect()
}
} else {
if !bleManager.mqttProxyConnected && node != nil {
bleManager.mqttManager.connectFromConfigSettings(node: node!)
}
}
}
.onChange(of: mapReportingEnabled) { _, newMapReportingEnabled in
if newMapReportingEnabled != node?.mqttConfig?.mapReportingEnabled { hasChanges = true }
}
.onChange(of: mapPublishIntervalSecs) { _, newMapPublishIntervalSecs in
if newMapPublishIntervalSecs != node?.mqttConfig?.mapPublishIntervalSecs ?? -1 { hasChanges = true }
}
}
.navigationTitle("mqtt.config")
@ -267,64 +324,6 @@ struct MQTTConfig: View {
)
}
)
.onChange(of: enabled) { _, newEnabled in
if newEnabled != node?.mqttConfig?.enabled { hasChanges = true }
}
.onChange(of: proxyToClientEnabled) { _, newProxyToClientEnabled in
if newProxyToClientEnabled {
jsonEnabled = false
tlsEnabled = false
}
if newProxyToClientEnabled != node?.mqttConfig?.proxyToClientEnabled { hasChanges = true }
}
.onChange(of: address) { newAddress in
if newAddress != node?.mqttConfig?.address ?? "" { hasChanges = true }
}
.onChange(of: username) { newUsername in
if newUsername != node?.mqttConfig?.username ?? "" { hasChanges = true }
}
.onChange(of: password) { newPassword in
if newPassword != node?.mqttConfig?.password ?? "" { hasChanges = true }
}
.onChange(of: root) { _, newRoot in
if newRoot != node?.mqttConfig?.root ?? "" { hasChanges = true }
}
.onChange(of: selectedTopic) { _, newSelectedTopic in
root = newSelectedTopic
}
.onChange(of: encryptionEnabled) { _, newEncryptionEnabled in
if newEncryptionEnabled != node?.mqttConfig?.encryptionEnabled { hasChanges = true }
}
.onChange(of: jsonEnabled) { _, newJsonEnabled in
if newJsonEnabled {
proxyToClientEnabled = false
}
if newJsonEnabled != node?.mqttConfig?.jsonEnabled { hasChanges = true }
}
.onChange(of: tlsEnabled) { _, newTlsEnabled in
if address.lowercased() == "mqtt.meshtastic.org" {
tlsEnabled = false
} else {
if newTlsEnabled != node?.mqttConfig?.tlsEnabled { hasChanges = true }
}
}
.onChange(of: mqttConnected) { _, newMqttConnected in
if newMqttConnected == false {
if bleManager.mqttProxyConnected {
bleManager.mqttManager.disconnect()
}
} else {
if !bleManager.mqttProxyConnected && node != nil {
bleManager.mqttManager.connectFromConfigSettings(node: node!)
}
}
}
.onChange(of: mapReportingEnabled) { _, newMapReportingEnabled in
if newMapReportingEnabled != node?.mqttConfig?.mapReportingEnabled { hasChanges = true }
}
.onChange(of: mapPublishIntervalSecs) { _, newMapPublishIntervalSecs in
if newMapPublishIntervalSecs != node?.mqttConfig?.mapPublishIntervalSecs ?? -1 { hasChanges = true }
}
.onFirstAppear {
// Need to request a MqttModuleConfig from the remote node before allowing changes
if let connectedPeripheral = bleManager.connectedPeripheral, let node {
@ -348,6 +347,7 @@ struct MQTTConfig: View {
}
}
}
func setMqttValues() {
nearbyTopics = []

View file

@ -24,66 +24,78 @@ struct NetworkConfig: View {
@State var ntpServer = ""
@State var ethEnabled = false
@State var ethMode = 0
@State var udpEnabled = false
var body: some View {
VStack {
Form {
ConfigHeader(title: "Network", config: \.networkConfig, node: node, onAppear: setNetworkValues)
if node != nil && node?.metadata?.hasWifi ?? false {
Section(header: Text("WiFi Options")) {
if let node {
if node.metadata?.hasWifi ?? false {
Section(header: Text("WiFi Options")) {
Toggle(isOn: $wifiEnabled) {
Label("enabled", systemImage: "wifi")
Text("Enabling WiFi will disable the bluetooth connection to the app.")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
Toggle(isOn: $wifiEnabled) {
Label("enabled", systemImage: "wifi")
Text("Enabling WiFi will disable the bluetooth connection to the app.")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
HStack {
Label("ssid", systemImage: "network")
TextField("ssid", text: $wifiSsid)
.foregroundColor(.gray)
.autocapitalization(.none)
.disableAutocorrection(true)
.onChange(of: wifiSsid) {
var totalBytes = wifiSsid.utf8.count
// Only mess with the value if it is too big
while totalBytes > 32 {
wifiSsid = String(wifiSsid.dropLast())
totalBytes = wifiSsid.utf8.count
HStack {
Label("ssid", systemImage: "network")
TextField("ssid", text: $wifiSsid)
.foregroundColor(.gray)
.autocapitalization(.none)
.disableAutocorrection(true)
.onChange(of: wifiSsid) {
var totalBytes = wifiSsid.utf8.count
// Only mess with the value if it is too big
while totalBytes > 32 {
wifiSsid = String(wifiSsid.dropLast())
totalBytes = wifiSsid.utf8.count
}
hasChanges = true
}
hasChanges = true
}
.foregroundColor(.gray)
}
.keyboardType(.default)
HStack {
Label("password", systemImage: "wallet.pass")
TextField("password", text: $wifiPsk)
.foregroundColor(.gray)
.autocapitalization(.none)
.disableAutocorrection(true)
.onChange(of: wifiPsk) {
var totalBytes = wifiPsk.utf8.count
// Only mess with the value if it is too big
while totalBytes > 63 {
wifiPsk = String(wifiPsk.dropLast())
totalBytes = wifiPsk.utf8.count
.foregroundColor(.gray)
}
.keyboardType(.default)
HStack {
Label("password", systemImage: "wallet.pass")
TextField("password", text: $wifiPsk)
.foregroundColor(.gray)
.autocapitalization(.none)
.disableAutocorrection(true)
.onChange(of: wifiPsk) {
var totalBytes = wifiPsk.utf8.count
// Only mess with the value if it is too big
while totalBytes > 63 {
wifiPsk = String(wifiPsk.dropLast())
totalBytes = wifiPsk.utf8.count
}
hasChanges = true
}
hasChanges = true
}
.foregroundColor(.gray)
.foregroundColor(.gray)
}
.keyboardType(.default)
}
.keyboardType(.default)
}
}
if node != nil && node?.metadata?.hasEthernet ?? false {
Section(header: Text("Ethernet Options")) {
Toggle(isOn: $ethEnabled) {
Label("enabled", systemImage: "network")
Text("Enabling Ethernet will disable the bluetooth connection to the app.")
if node.metadata?.hasEthernet ?? false {
Section(header: Text("Ethernet Options")) {
Toggle(isOn: $ethEnabled) {
Label("enabled", systemImage: "network")
Text("Enabling Ethernet will disable the bluetooth connection to the app.")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
}
}
if node.metadata?.hasEthernet ?? false || node.metadata?.hasWifi ?? false {
Section(header: Text("UDP Broadcast")) {
Toggle(isOn: $udpEnabled) {
Label("enabled", systemImage: "point.3.connected.trianglepath.dotted")
Text("Enable broadcasting packets via UDP over the local network.")
}
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
}
}
}
@ -98,6 +110,7 @@ struct NetworkConfig: View {
network.wifiSsid = self.wifiSsid
network.wifiPsk = self.wifiPsk
network.ethEnabled = self.ethEnabled
network.enabledProtocols = self.udpEnabled ? UInt32(Config.NetworkConfig.ProtocolFlags.udpBroadcast.rawValue) : UInt32(Config.NetworkConfig.ProtocolFlags.noBroadcast.rawValue)
// network.addressMode = Config.NetworkConfig.AddressMode.dhcp
let adminMessageId = bleManager.saveNetworkConfig(config: network, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
@ -166,14 +179,30 @@ struct NetworkConfig: View {
}
.onChange(of: ethEnabled) { _, newEthEnabled in
if newEthEnabled != node?.networkConfig?.ethEnabled { hasChanges = true }
}.onChange(of: udpEnabled) {_, newUdpEnabled in
if let netConfig = node?.networkConfig {
let newValue: UInt32
if newUdpEnabled {
newValue = UInt32(netConfig.enabledProtocols) | UInt32(Config.NetworkConfig.ProtocolFlags.udpBroadcast.rawValue)
} else {
newValue = UInt32(netConfig.enabledProtocols) & ~UInt32(Config.NetworkConfig.ProtocolFlags.udpBroadcast.rawValue)
}
if netConfig.enabledProtocols != Int32(newValue) {
netConfig.enabledProtocols = Int32(newValue)
hasChanges = true
}
}
}
}
func setNetworkValues() {
self.wifiEnabled = node?.networkConfig?.wifiEnabled ?? false
self.wifiSsid = node?.networkConfig?.wifiSsid ?? ""
self.wifiPsk = node?.networkConfig?.wifiPsk ?? ""
self.wifiMode = Int(node?.networkConfig?.wifiMode ?? 0)
self.ethEnabled = node?.networkConfig?.ethEnabled ?? false
let enabledProtocols = UInt32(node?.networkConfig?.enabledProtocols ?? Int32(Config.NetworkConfig.ProtocolFlags.noBroadcast.rawValue))
self.udpEnabled = enabledProtocols & UInt32(Config.NetworkConfig.ProtocolFlags.udpBroadcast.rawValue) != 0
self.hasChanges = false
}
}

View file

@ -25,9 +25,9 @@ struct SecurityConfig: View {
@State var hasValidPublicKey: Bool = false
@State var privateKey = ""
@State var hasValidPrivateKey: Bool = false
@State var adminKey = ""
@State var adminKey2 = ""
@State var adminKey3 = ""
@State var adminKey: String = ""
@State var adminKey2: String = ""
@State var adminKey3: String = ""
@State var hasValidAdminKey: Bool = true
@State var hasValidAdminKey2: Bool = true
@State var hasValidAdminKey3: Bool = true
@ -259,9 +259,9 @@ struct SecurityConfig: View {
func setSecurityValues() {
self.publicKey = node?.securityConfig?.publicKey?.base64EncodedString() ?? ""
self.privateKey = node?.securityConfig?.privateKey?.base64EncodedString() ?? ""
self.adminKey = node?.securityConfig?.adminKey?.base64EncodedString() ?? ""
self.adminKey2 = node?.securityConfig?.adminKey2?.base64EncodedString() ?? ""
self.adminKey3 = node?.securityConfig?.adminKey3?.base64EncodedString() ?? ""
self.adminKey = node?.securityConfig?.adminKey?.base64EncodedString(options: .lineLength64Characters) ?? ""
self.adminKey2 = node?.securityConfig?.adminKey2?.base64EncodedString(options: .lineLength64Characters) ?? ""
self.adminKey3 = node?.securityConfig?.adminKey3?.base64EncodedString(options: .lineLength64Characters) ?? ""
self.isManaged = node?.securityConfig?.isManaged ?? false
self.serialEnabled = node?.securityConfig?.serialEnabled ?? false
self.debugLogApiEnabled = node?.securityConfig?.debugLogApiEnabled ?? false

View file

@ -138,7 +138,7 @@ struct Firmware: View {
.font(.callout)
.padding(.bottom)
} else {
Text("OTA Updates are not supported on the this NRF Device.")
Text("OTA Updates are not supported on this NRF Device.")
.font(.title3)
Link("Drag & Drop Firmware Update", destination: URL(string: "https://meshtastic.org/docs/getting-started/flashing-firmware/nrf52/drag-n-drop")!)
.font(.callout)

View file

@ -8,6 +8,7 @@
import SwiftUI
import OSLog
import TipKit
import MeshtasticProtobufs
struct Settings: View {
@Environment(\.managedObjectContext) var context
@ -25,10 +26,21 @@ struct Settings: View {
@State private var selectedNode: Int = 0
@State private var preferredNodeNum: Int = 0
@State private var moduleOverride: Bool = false
@ObservedObject
var router: Router
// MARK: Helper
private func isModuleSupported(_ module: ExcludedModules) -> Bool {
return moduleOverride || Int(nodes.first(where: { $0.num == preferredNodeNum })?.metadata?.excludedModules ?? Int32.zero) & module.rawValue == 0
}
private func isAnySupported(_ modules: [ExcludedModules]) -> Bool {
return modules.map(isModuleSupported).contains(true)
}
// MARK: Views
var radioConfigurationSection: some View {
@ -153,57 +165,68 @@ struct Settings: View {
}
var moduleConfigurationSection: some View {
Section("module.configuration") {
NavigationLink(value: SettingsNavigationState.ambientLighting) {
Label {
Text("Ambient Lighting")
} icon: {
Image(systemName: "light.max")
Section {
if isModuleSupported(.ambientlightingConfig) {
NavigationLink(value: SettingsNavigationState.ambientLighting) {
Label {
Text("Ambient Lighting")
} icon: {
Image(systemName: "light.max")
}
}
}
NavigationLink(value: SettingsNavigationState.cannedMessages) {
Label {
Text("Canned Messages")
} icon: {
Image(systemName: "list.bullet.rectangle.fill")
if isModuleSupported(.cannedmsgConfig) {
NavigationLink(value: SettingsNavigationState.cannedMessages) {
Label {
Text("Canned Messages")
} icon: {
Image(systemName: "list.bullet.rectangle.fill")
}
}
}
NavigationLink(value: SettingsNavigationState.detectionSensor) {
Label {
Text("detection.sensor")
} icon: {
Image(systemName: "sensor")
if isModuleSupported(.detectionsensorConfig) {
NavigationLink(value: SettingsNavigationState.detectionSensor) {
Label {
Text("detection.sensor")
} icon: {
Image(systemName: "sensor")
}
}
}
NavigationLink(value: SettingsNavigationState.externalNotification) {
Label {
Text("external.notification")
} icon: {
Image(systemName: "megaphone")
if isModuleSupported(.extnotifConfig) {
NavigationLink(value: SettingsNavigationState.externalNotification) {
Label {
Text("external.notification")
} icon: {
Image(systemName: "megaphone")
}
}
}
NavigationLink(value: SettingsNavigationState.mqtt) {
Label {
Text("mqtt")
} icon: {
Image(systemName: "dot.radiowaves.up.forward")
if isModuleSupported(.mqttConfig) {
NavigationLink(value: SettingsNavigationState.mqtt) {
Label {
Text("mqtt")
} icon: {
Image(systemName: "dot.radiowaves.up.forward")
}
}
}
NavigationLink(value: SettingsNavigationState.rangeTest) {
Label {
Text("range.test")
} icon: {
Image(systemName: "point.3.connected.trianglepath.dotted")
if isModuleSupported(.rangetestConfig) {
NavigationLink(value: SettingsNavigationState.rangeTest) {
Label {
Text("range.test")
} icon: {
Image(systemName: "point.3.connected.trianglepath.dotted")
}
}
}
if let node = nodes.first(where: { $0.num == preferredNodeNum }),
node.metadata?.hasWifi ?? false {
if isModuleSupported(.paxcounterConfig) {
NavigationLink(value: SettingsNavigationState.paxCounter) {
Label {
Text("config.module.paxcounter.settings")
@ -213,37 +236,61 @@ struct Settings: View {
}
}
NavigationLink(value: SettingsNavigationState.ringtone) {
Label {
Text("ringtone")
} icon: {
Image(systemName: "music.note.list")
if isModuleSupported(.audioConfig) {
NavigationLink(value: SettingsNavigationState.ringtone) {
Label {
Text("ringtone")
} icon: {
Image(systemName: "music.note.list")
}
}
}
NavigationLink(value: SettingsNavigationState.serial) {
Label {
Text("serial")
} icon: {
Image(systemName: "terminal")
if isModuleSupported(.serialConfig) {
NavigationLink(value: SettingsNavigationState.serial) {
Label {
Text("serial")
} icon: {
Image(systemName: "terminal")
}
}
}
NavigationLink(value: SettingsNavigationState.storeAndForward) {
Label {
Text("Store & Forward")
} icon: {
Image(systemName: "envelope.arrow.triangle.branch")
if isModuleSupported(.storeforwardConfig) {
NavigationLink(value: SettingsNavigationState.storeAndForward) {
Label {
Text("Store & Forward")
} icon: {
Image(systemName: "envelope.arrow.triangle.branch")
}
}
}
NavigationLink(value: SettingsNavigationState.telemetry) {
Label {
Text("telemetry")
} icon: {
Image(systemName: "chart.xyaxis.line")
if isModuleSupported(.telemetryConfig) {
NavigationLink(value: SettingsNavigationState.telemetry) {
Label {
Text("telemetry")
} icon: {
Image(systemName: "chart.xyaxis.line")
}
}
}
// Update this list with the modules that are shown above. If all are not supported
// Then show a message.
if !isAnySupported([.ambientlightingConfig, .cannedmsgConfig,
.detectionsensorConfig, .extnotifConfig,
.mqttConfig, .rangetestConfig, .paxcounterConfig,
.audioConfig, .serialConfig, .storeforwardConfig,
.telemetryConfig]) {
Text("This node does not support any configurable modules.")
}
} header: {
Text("module.configuration")
} footer: {
if moduleOverride {
Text("Currently showing modules that may not be supported by this node.")
}
}
}
@ -341,7 +388,7 @@ struct Settings: View {
/// Connected Node
if node.num == bleManager.connectedPeripheral?.num ?? 0 {
Label {
Text("BLE: \(node.user?.longName ?? "unknown".localized)")
Text("BLE: \(node.user?.longName?.addingVariationSelectors ?? "unknown".localized)")
} icon: {
Image(systemName: "antenna.radiowaves.left.and.right")
}
@ -363,14 +410,14 @@ struct Settings: View {
.tag(Int(node.num))
} else if UserDefaults.enableAdministration && node.user?.pkiEncrypted ?? false {
Label {
Text("Request PKI Admin: \(node.user?.longName ?? "unknown".localized)")
Text("Request PKI Admin: \(node.user?.longName?.addingVariationSelectors ?? "unknown".localized)")
} icon: {
Image(systemName: "rectangle.and.hand.point.up.left")
}
.tag(Int(node.num))
} else if !UserDefaults.enableAdministration {
Label {
Text("Request Legacy Admin: \(node.user?.longName ?? "unknown".localized)")
Text("Request Legacy Admin: \(node.user?.longName?.addingVariationSelectors ?? "unknown".localized)")
} icon: {
Image(systemName: "rectangle.and.hand.point.up.left")
}
@ -395,7 +442,7 @@ struct Settings: View {
TipView(AdminChannelTip(), arrowEdge: .top)
} else {
if bleManager.connectedPeripheral != nil {
Text("Connected Node \(node?.user?.longName ?? "unknown".localized)")
Text("Connected Node \(node?.user?.longName?.addingVariationSelectors ?? "unknown".localized)")
}
}
}
@ -497,7 +544,10 @@ struct Settings: View {
}
.navigationTitle("settings")
.navigationBarItems(
leading: MeshtasticLogo()
leading: MeshtasticLogo().onLongPressGesture(minimumDuration: 1.0) {
self.moduleOverride.toggle()
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
}
)
}
}

View file

@ -50,12 +50,14 @@ struct UserConfig: View {
TextField("Long Name", text: $longName)
.onChange(of: longName) {
var totalBytes = longName.utf8.count
var newValue = longName.withoutVariationSelectors
var totalBytes = newValue.utf8.count
// Only mess with the value if it is too big
while totalBytes > (isLicensed ? 6 : 36) {
longName = String(longName.dropLast())
totalBytes = longName.utf8.count
newValue = String(newValue.dropLast())
totalBytes = newValue.utf8.count
}
longName = newValue
}
}
.keyboardType(.default)

View file

@ -1,5 +1,6 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/admin.proto
@ -24,7 +25,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
/// This message is handled by the Admin module and is responsible for all settings/channel read/write operations.
/// This message is used to do settings operations to both remote AND local nodes.
/// (Prior to 1.2 these operations were done via special ToRadio operations)
public struct AdminMessage {
public struct AdminMessage: @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.
@ -261,6 +262,36 @@ public struct AdminMessage {
set {payloadVariant = .setScale(newValue)}
}
///
/// Backup the node's preferences
public var backupPreferences: AdminMessage.BackupLocation {
get {
if case .backupPreferences(let v)? = payloadVariant {return v}
return .flash
}
set {payloadVariant = .backupPreferences(newValue)}
}
///
/// Restore the node's preferences
public var restorePreferences: AdminMessage.BackupLocation {
get {
if case .restorePreferences(let v)? = payloadVariant {return v}
return .flash
}
set {payloadVariant = .restorePreferences(newValue)}
}
///
/// Remove backups of the node's preferences
public var removeBackupPreferences: AdminMessage.BackupLocation {
get {
if case .removeBackupPreferences(let v)? = payloadVariant {return v}
return .flash
}
set {payloadVariant = .removeBackupPreferences(newValue)}
}
///
/// Set the owner for this node
public var setOwner: User {
@ -533,7 +564,7 @@ public struct AdminMessage {
///
/// TODO: REPLACE
public enum OneOf_PayloadVariant: Equatable {
public enum OneOf_PayloadVariant: Equatable, Sendable {
///
/// Send the specified channel in the response to this message
/// NOTE: This field is sent with the channel index + 1 (to ensure we never try to send 'zero' - which protobufs treats as not present)
@ -603,6 +634,15 @@ public struct AdminMessage {
/// Set zero and offset for scale chips
case setScale(UInt32)
///
/// Backup the node's preferences
case backupPreferences(AdminMessage.BackupLocation)
///
/// Restore the node's preferences
case restorePreferences(AdminMessage.BackupLocation)
///
/// Remove backups of the node's preferences
case removeBackupPreferences(AdminMessage.BackupLocation)
///
/// Set the owner for this node
case setOwner(User)
///
@ -689,213 +729,11 @@ public struct AdminMessage {
/// Tell the node to reset the nodedb.
case nodedbReset(Int32)
#if !swift(>=4.1)
public static func ==(lhs: AdminMessage.OneOf_PayloadVariant, rhs: AdminMessage.OneOf_PayloadVariant) -> Bool {
// 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 (lhs, rhs) {
case (.getChannelRequest, .getChannelRequest): return {
guard case .getChannelRequest(let l) = lhs, case .getChannelRequest(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getChannelResponse, .getChannelResponse): return {
guard case .getChannelResponse(let l) = lhs, case .getChannelResponse(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getOwnerRequest, .getOwnerRequest): return {
guard case .getOwnerRequest(let l) = lhs, case .getOwnerRequest(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getOwnerResponse, .getOwnerResponse): return {
guard case .getOwnerResponse(let l) = lhs, case .getOwnerResponse(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getConfigRequest, .getConfigRequest): return {
guard case .getConfigRequest(let l) = lhs, case .getConfigRequest(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getConfigResponse, .getConfigResponse): return {
guard case .getConfigResponse(let l) = lhs, case .getConfigResponse(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getModuleConfigRequest, .getModuleConfigRequest): return {
guard case .getModuleConfigRequest(let l) = lhs, case .getModuleConfigRequest(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getModuleConfigResponse, .getModuleConfigResponse): return {
guard case .getModuleConfigResponse(let l) = lhs, case .getModuleConfigResponse(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getCannedMessageModuleMessagesRequest, .getCannedMessageModuleMessagesRequest): return {
guard case .getCannedMessageModuleMessagesRequest(let l) = lhs, case .getCannedMessageModuleMessagesRequest(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getCannedMessageModuleMessagesResponse, .getCannedMessageModuleMessagesResponse): return {
guard case .getCannedMessageModuleMessagesResponse(let l) = lhs, case .getCannedMessageModuleMessagesResponse(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getDeviceMetadataRequest, .getDeviceMetadataRequest): return {
guard case .getDeviceMetadataRequest(let l) = lhs, case .getDeviceMetadataRequest(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getDeviceMetadataResponse, .getDeviceMetadataResponse): return {
guard case .getDeviceMetadataResponse(let l) = lhs, case .getDeviceMetadataResponse(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getRingtoneRequest, .getRingtoneRequest): return {
guard case .getRingtoneRequest(let l) = lhs, case .getRingtoneRequest(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getRingtoneResponse, .getRingtoneResponse): return {
guard case .getRingtoneResponse(let l) = lhs, case .getRingtoneResponse(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getDeviceConnectionStatusRequest, .getDeviceConnectionStatusRequest): return {
guard case .getDeviceConnectionStatusRequest(let l) = lhs, case .getDeviceConnectionStatusRequest(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getDeviceConnectionStatusResponse, .getDeviceConnectionStatusResponse): return {
guard case .getDeviceConnectionStatusResponse(let l) = lhs, case .getDeviceConnectionStatusResponse(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.setHamMode, .setHamMode): return {
guard case .setHamMode(let l) = lhs, case .setHamMode(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getNodeRemoteHardwarePinsRequest, .getNodeRemoteHardwarePinsRequest): return {
guard case .getNodeRemoteHardwarePinsRequest(let l) = lhs, case .getNodeRemoteHardwarePinsRequest(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getNodeRemoteHardwarePinsResponse, .getNodeRemoteHardwarePinsResponse): return {
guard case .getNodeRemoteHardwarePinsResponse(let l) = lhs, case .getNodeRemoteHardwarePinsResponse(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.enterDfuModeRequest, .enterDfuModeRequest): return {
guard case .enterDfuModeRequest(let l) = lhs, case .enterDfuModeRequest(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.deleteFileRequest, .deleteFileRequest): return {
guard case .deleteFileRequest(let l) = lhs, case .deleteFileRequest(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.setScale, .setScale): return {
guard case .setScale(let l) = lhs, case .setScale(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.setOwner, .setOwner): return {
guard case .setOwner(let l) = lhs, case .setOwner(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.setChannel, .setChannel): return {
guard case .setChannel(let l) = lhs, case .setChannel(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.setConfig, .setConfig): return {
guard case .setConfig(let l) = lhs, case .setConfig(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.setModuleConfig, .setModuleConfig): return {
guard case .setModuleConfig(let l) = lhs, case .setModuleConfig(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.setCannedMessageModuleMessages, .setCannedMessageModuleMessages): return {
guard case .setCannedMessageModuleMessages(let l) = lhs, case .setCannedMessageModuleMessages(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.setRingtoneMessage, .setRingtoneMessage): return {
guard case .setRingtoneMessage(let l) = lhs, case .setRingtoneMessage(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.removeByNodenum, .removeByNodenum): return {
guard case .removeByNodenum(let l) = lhs, case .removeByNodenum(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.setFavoriteNode, .setFavoriteNode): return {
guard case .setFavoriteNode(let l) = lhs, case .setFavoriteNode(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.removeFavoriteNode, .removeFavoriteNode): return {
guard case .removeFavoriteNode(let l) = lhs, case .removeFavoriteNode(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.setFixedPosition, .setFixedPosition): return {
guard case .setFixedPosition(let l) = lhs, case .setFixedPosition(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.removeFixedPosition, .removeFixedPosition): return {
guard case .removeFixedPosition(let l) = lhs, case .removeFixedPosition(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.setTimeOnly, .setTimeOnly): return {
guard case .setTimeOnly(let l) = lhs, case .setTimeOnly(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getUiConfigRequest, .getUiConfigRequest): return {
guard case .getUiConfigRequest(let l) = lhs, case .getUiConfigRequest(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getUiConfigResponse, .getUiConfigResponse): return {
guard case .getUiConfigResponse(let l) = lhs, case .getUiConfigResponse(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.storeUiConfig, .storeUiConfig): return {
guard case .storeUiConfig(let l) = lhs, case .storeUiConfig(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.setIgnoredNode, .setIgnoredNode): return {
guard case .setIgnoredNode(let l) = lhs, case .setIgnoredNode(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.removeIgnoredNode, .removeIgnoredNode): return {
guard case .removeIgnoredNode(let l) = lhs, case .removeIgnoredNode(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.beginEditSettings, .beginEditSettings): return {
guard case .beginEditSettings(let l) = lhs, case .beginEditSettings(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.commitEditSettings, .commitEditSettings): return {
guard case .commitEditSettings(let l) = lhs, case .commitEditSettings(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.factoryResetDevice, .factoryResetDevice): return {
guard case .factoryResetDevice(let l) = lhs, case .factoryResetDevice(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.rebootOtaSeconds, .rebootOtaSeconds): return {
guard case .rebootOtaSeconds(let l) = lhs, case .rebootOtaSeconds(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.exitSimulator, .exitSimulator): return {
guard case .exitSimulator(let l) = lhs, case .exitSimulator(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.rebootSeconds, .rebootSeconds): return {
guard case .rebootSeconds(let l) = lhs, case .rebootSeconds(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.shutdownSeconds, .shutdownSeconds): return {
guard case .shutdownSeconds(let l) = lhs, case .shutdownSeconds(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.factoryResetConfig, .factoryResetConfig): return {
guard case .factoryResetConfig(let l) = lhs, case .factoryResetConfig(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.nodedbReset, .nodedbReset): return {
guard case .nodedbReset(let l) = lhs, case .nodedbReset(let r) = rhs else { preconditionFailure() }
return l == r
}()
default: return false
}
}
#endif
}
///
/// TODO: REPLACE
public enum ConfigType: SwiftProtobuf.Enum {
public enum ConfigType: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -929,6 +767,9 @@ public struct AdminMessage {
///
/// TODO: REPLACE
case securityConfig // = 7
///
/// Session key config
case sessionkeyConfig // = 8
///
@ -972,11 +813,25 @@ public struct AdminMessage {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [AdminMessage.ConfigType] = [
.deviceConfig,
.positionConfig,
.powerConfig,
.networkConfig,
.displayConfig,
.loraConfig,
.bluetoothConfig,
.securityConfig,
.sessionkeyConfig,
.deviceuiConfig,
]
}
///
/// TODO: REPLACE
public enum ModuleConfigType: SwiftProtobuf.Enum {
public enum ModuleConfigType: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -1074,53 +929,71 @@ public struct AdminMessage {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [AdminMessage.ModuleConfigType] = [
.mqttConfig,
.serialConfig,
.extnotifConfig,
.storeforwardConfig,
.rangetestConfig,
.telemetryConfig,
.cannedmsgConfig,
.audioConfig,
.remotehardwareConfig,
.neighborinfoConfig,
.ambientlightingConfig,
.detectionsensorConfig,
.paxcounterConfig,
]
}
public enum BackupLocation: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
/// Backup to the internal flash
case flash // = 0
///
/// Backup to the SD card
case sd // = 1
case UNRECOGNIZED(Int)
public init() {
self = .flash
}
public init?(rawValue: Int) {
switch rawValue {
case 0: self = .flash
case 1: self = .sd
default: self = .UNRECOGNIZED(rawValue)
}
}
public var rawValue: Int {
switch self {
case .flash: return 0
case .sd: return 1
case .UNRECOGNIZED(let i): return i
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [AdminMessage.BackupLocation] = [
.flash,
.sd,
]
}
public init() {}
}
#if swift(>=4.2)
extension AdminMessage.ConfigType: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [AdminMessage.ConfigType] = [
.deviceConfig,
.positionConfig,
.powerConfig,
.networkConfig,
.displayConfig,
.loraConfig,
.bluetoothConfig,
.securityConfig,
.sessionkeyConfig,
.deviceuiConfig,
]
}
extension AdminMessage.ModuleConfigType: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [AdminMessage.ModuleConfigType] = [
.mqttConfig,
.serialConfig,
.extnotifConfig,
.storeforwardConfig,
.rangetestConfig,
.telemetryConfig,
.cannedmsgConfig,
.audioConfig,
.remotehardwareConfig,
.neighborinfoConfig,
.ambientlightingConfig,
.detectionsensorConfig,
.paxcounterConfig,
]
}
#endif // swift(>=4.2)
///
/// Parameters for setting up Meshtastic for ameteur radio usage
public struct HamParameters {
public struct HamParameters: 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.
@ -1150,7 +1023,7 @@ public struct HamParameters {
///
/// Response envelope for node_remote_hardware_pins
public struct NodeRemoteHardwarePinsResponse {
public struct NodeRemoteHardwarePinsResponse: 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.
@ -1164,15 +1037,6 @@ public struct NodeRemoteHardwarePinsResponse {
public init() {}
}
#if swift(>=5.5) && canImport(_Concurrency)
extension AdminMessage: @unchecked Sendable {}
extension AdminMessage.OneOf_PayloadVariant: @unchecked Sendable {}
extension AdminMessage.ConfigType: @unchecked Sendable {}
extension AdminMessage.ModuleConfigType: @unchecked Sendable {}
extension HamParameters: @unchecked Sendable {}
extension NodeRemoteHardwarePinsResponse: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "meshtastic"
@ -1203,6 +1067,9 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
21: .standard(proto: "enter_dfu_mode_request"),
22: .standard(proto: "delete_file_request"),
23: .standard(proto: "set_scale"),
24: .standard(proto: "backup_preferences"),
25: .standard(proto: "restore_preferences"),
26: .standard(proto: "remove_backup_preferences"),
32: .standard(proto: "set_owner"),
33: .standard(proto: "set_channel"),
34: .standard(proto: "set_config"),
@ -1453,6 +1320,30 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
self.payloadVariant = .setScale(v)
}
}()
case 24: try {
var v: AdminMessage.BackupLocation?
try decoder.decodeSingularEnumField(value: &v)
if let v = v {
if self.payloadVariant != nil {try decoder.handleConflictingOneOf()}
self.payloadVariant = .backupPreferences(v)
}
}()
case 25: try {
var v: AdminMessage.BackupLocation?
try decoder.decodeSingularEnumField(value: &v)
if let v = v {
if self.payloadVariant != nil {try decoder.handleConflictingOneOf()}
self.payloadVariant = .restorePreferences(v)
}
}()
case 26: try {
var v: AdminMessage.BackupLocation?
try decoder.decodeSingularEnumField(value: &v)
if let v = v {
if self.payloadVariant != nil {try decoder.handleConflictingOneOf()}
self.payloadVariant = .removeBackupPreferences(v)
}
}()
case 32: try {
var v: User?
var hadOneofValue = false
@ -1796,6 +1687,18 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
guard case .setScale(let v)? = self.payloadVariant else { preconditionFailure() }
try visitor.visitSingularUInt32Field(value: v, fieldNumber: 23)
}()
case .backupPreferences?: try {
guard case .backupPreferences(let v)? = self.payloadVariant else { preconditionFailure() }
try visitor.visitSingularEnumField(value: v, fieldNumber: 24)
}()
case .restorePreferences?: try {
guard case .restorePreferences(let v)? = self.payloadVariant else { preconditionFailure() }
try visitor.visitSingularEnumField(value: v, fieldNumber: 25)
}()
case .removeBackupPreferences?: try {
guard case .removeBackupPreferences(let v)? = self.payloadVariant else { preconditionFailure() }
try visitor.visitSingularEnumField(value: v, fieldNumber: 26)
}()
case .setOwner?: try {
guard case .setOwner(let v)? = self.payloadVariant else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 32)
@ -1949,6 +1852,13 @@ extension AdminMessage.ModuleConfigType: SwiftProtobuf._ProtoNameProviding {
]
}
extension AdminMessage.BackupLocation: SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "FLASH"),
1: .same(proto: "SD"),
]
}
extension HamParameters: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".HamParameters"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
@ -1980,7 +1890,7 @@ extension HamParameters: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa
if self.txPower != 0 {
try visitor.visitSingularInt32Field(value: self.txPower, fieldNumber: 2)
}
if self.frequency != 0 {
if self.frequency.bitPattern != 0 {
try visitor.visitSingularFloatField(value: self.frequency, fieldNumber: 3)
}
if !self.shortName.isEmpty {

View file

@ -1,5 +1,6 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/apponly.proto
@ -7,7 +8,6 @@
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
@ -26,7 +26,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
/// any SECONDARY channels.
/// No DISABLED channels are included.
/// This abstraction is used only on the the 'app side' of the world (ie python, javascript and android etc) to show a group of Channels as a (long) URL
public struct ChannelSet {
public struct ChannelSet: 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.
@ -53,10 +53,6 @@ public struct ChannelSet {
fileprivate var _loraConfig: Config.LoRaConfig? = nil
}
#if swift(>=5.5) && canImport(_Concurrency)
extension ChannelSet: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "meshtastic"

View file

@ -1,5 +1,6 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/atak.proto
@ -20,7 +21,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
typealias Version = _2
}
public enum Team: SwiftProtobuf.Enum {
public enum Team: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -130,11 +131,6 @@ public enum Team: SwiftProtobuf.Enum {
}
}
}
#if swift(>=4.2)
extension Team: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Team] = [
.unspecifedColor,
@ -153,13 +149,12 @@ extension Team: CaseIterable {
.darkGreen,
.brown,
]
}
#endif // swift(>=4.2)
}
///
/// Role of the group member
public enum MemberRole: SwiftProtobuf.Enum {
public enum MemberRole: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -233,11 +228,6 @@ public enum MemberRole: SwiftProtobuf.Enum {
}
}
}
#if swift(>=4.2)
extension MemberRole: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [MemberRole] = [
.unspecifed,
@ -250,13 +240,12 @@ extension MemberRole: CaseIterable {
.rto,
.k9,
]
}
#endif // swift(>=4.2)
}
///
/// Packets for the official ATAK Plugin
public struct TAKPacket {
public struct TAKPacket: @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.
@ -337,7 +326,7 @@ public struct TAKPacket {
///
/// The payload of the packet
public enum OneOf_PayloadVariant: Equatable {
public enum OneOf_PayloadVariant: Equatable, @unchecked Sendable {
///
/// TAK position report
case pli(PLI)
@ -349,28 +338,6 @@ public struct TAKPacket {
/// May be compressed / truncated by the sender (EUD)
case detail(Data)
#if !swift(>=4.1)
public static func ==(lhs: TAKPacket.OneOf_PayloadVariant, rhs: TAKPacket.OneOf_PayloadVariant) -> Bool {
// 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 (lhs, rhs) {
case (.pli, .pli): return {
guard case .pli(let l) = lhs, case .pli(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.chat, .chat): return {
guard case .chat(let l) = lhs, case .chat(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.detail, .detail): return {
guard case .detail(let l) = lhs, case .detail(let r) = rhs else { preconditionFailure() }
return l == r
}()
default: return false
}
}
#endif
}
public init() {}
@ -382,7 +349,7 @@ public struct TAKPacket {
///
/// ATAK GeoChat message
public struct GeoChat {
public struct GeoChat: 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.
@ -424,7 +391,7 @@ public struct GeoChat {
///
/// ATAK Group
/// <__group role='Team Member' name='Cyan'/>
public struct Group {
public struct Group: 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.
@ -446,7 +413,7 @@ public struct Group {
///
/// ATAK EUD Status
/// <status battery='100' />
public struct Status {
public struct Status: 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.
@ -463,7 +430,7 @@ public struct Status {
///
/// ATAK Contact
/// <contact endpoint='0.0.0.0:4242:tcp' phone='+12345678' callsign='FALKE'/>
public struct Contact {
public struct Contact: 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.
@ -483,7 +450,7 @@ public struct Contact {
///
/// Position Location Information from ATAK
public struct PLI {
public struct PLI: 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.
@ -515,18 +482,6 @@ public struct PLI {
public init() {}
}
#if swift(>=5.5) && canImport(_Concurrency)
extension Team: @unchecked Sendable {}
extension MemberRole: @unchecked Sendable {}
extension TAKPacket: @unchecked Sendable {}
extension TAKPacket.OneOf_PayloadVariant: @unchecked Sendable {}
extension GeoChat: @unchecked Sendable {}
extension Group: @unchecked Sendable {}
extension Status: @unchecked Sendable {}
extension Contact: @unchecked Sendable {}
extension PLI: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "meshtastic"

View file

@ -1,5 +1,6 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/cannedmessages.proto
@ -7,7 +8,6 @@
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
///
/// Canned message module configuration.
public struct CannedMessageModuleConfig {
public struct CannedMessageModuleConfig: 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.
@ -36,10 +36,6 @@ public struct CannedMessageModuleConfig {
public init() {}
}
#if swift(>=5.5) && canImport(_Concurrency)
extension CannedMessageModuleConfig: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "meshtastic"

View file

@ -1,5 +1,6 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/channel.proto
@ -36,13 +37,15 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
/// FIXME: Add description of multi-channel support and how primary vs secondary channels are used.
/// FIXME: explain how apps use channels for security.
/// explain how remote settings and remote gpio are managed as an example
public struct ChannelSettings {
public struct ChannelSettings: @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.
///
/// Deprecated in favor of LoraConfig.channel_num
///
/// NOTE: This field was marked as deprecated in the .proto file.
public var channelNum: UInt32 = 0
///
@ -111,7 +114,7 @@ public struct ChannelSettings {
///
/// This message is specifically for modules to store per-channel configuration data.
public struct ModuleSettings {
public struct ModuleSettings: 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.
@ -132,7 +135,7 @@ public struct ModuleSettings {
///
/// A pair of a channel number, mode and the (sharable) settings for that channel
public struct Channel {
public struct Channel: 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.
@ -170,7 +173,7 @@ public struct Channel {
/// cross band routing as needed.
/// If a device has only a single radio (the common case) only one channel can be PRIMARY at a time
/// (but any number of SECONDARY channels can't be sent received on that common frequency)
public enum Role: SwiftProtobuf.Enum {
public enum Role: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -209,6 +212,13 @@ public struct Channel {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Channel.Role] = [
.disabled,
.primary,
.secondary,
]
}
public init() {}
@ -216,26 +226,6 @@ public struct Channel {
fileprivate var _settings: ChannelSettings? = nil
}
#if swift(>=4.2)
extension Channel.Role: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Channel.Role] = [
.disabled,
.primary,
.secondary,
]
}
#endif // swift(>=4.2)
#if swift(>=5.5) && canImport(_Concurrency)
extension ChannelSettings: @unchecked Sendable {}
extension ModuleSettings: @unchecked Sendable {}
extension Channel: @unchecked Sendable {}
extension Channel.Role: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "meshtastic"

View file

@ -1,5 +1,6 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/clientonly.proto
@ -7,7 +8,6 @@
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
@ -23,7 +23,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
///
/// This abstraction is used to contain any configuration for provisioning a node on any client.
/// It is useful for importing and exporting configurations.
public struct DeviceProfile {
public struct DeviceProfile: 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.
@ -130,10 +130,6 @@ public struct DeviceProfile {
fileprivate var _cannedMessages: String? = nil
}
#if swift(>=5.5) && canImport(_Concurrency)
extension DeviceProfile: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "meshtastic"

View file

@ -1,5 +1,6 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/config.proto
@ -20,7 +21,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
typealias Version = _2
}
public struct Config {
public struct Config: 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.
@ -113,7 +114,7 @@ public struct Config {
///
/// Payload Variant
public enum OneOf_PayloadVariant: Equatable {
public enum OneOf_PayloadVariant: Equatable, Sendable {
case device(Config.DeviceConfig)
case position(Config.PositionConfig)
case power(Config.PowerConfig)
@ -125,61 +126,11 @@ public struct Config {
case sessionkey(Config.SessionkeyConfig)
case deviceUi(DeviceUIConfig)
#if !swift(>=4.1)
public static func ==(lhs: Config.OneOf_PayloadVariant, rhs: Config.OneOf_PayloadVariant) -> Bool {
// 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 (lhs, rhs) {
case (.device, .device): return {
guard case .device(let l) = lhs, case .device(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.position, .position): return {
guard case .position(let l) = lhs, case .position(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.power, .power): return {
guard case .power(let l) = lhs, case .power(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.network, .network): return {
guard case .network(let l) = lhs, case .network(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.display, .display): return {
guard case .display(let l) = lhs, case .display(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.lora, .lora): return {
guard case .lora(let l) = lhs, case .lora(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.bluetooth, .bluetooth): return {
guard case .bluetooth(let l) = lhs, case .bluetooth(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.security, .security): return {
guard case .security(let l) = lhs, case .security(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.sessionkey, .sessionkey): return {
guard case .sessionkey(let l) = lhs, case .sessionkey(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.deviceUi, .deviceUi): return {
guard case .deviceUi(let l) = lhs, case .deviceUi(let r) = rhs else { preconditionFailure() }
return l == r
}()
default: return false
}
}
#endif
}
///
/// Configuration
public struct DeviceConfig {
public struct DeviceConfig: 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.
@ -191,6 +142,8 @@ public struct Config {
///
/// Disabling this will disable the SerialConsole by not initilizing the StreamAPI
/// Moved to SecurityConfig
///
/// NOTE: This field was marked as deprecated in the .proto file.
public var serialEnabled: Bool = false
///
@ -220,6 +173,8 @@ public struct Config {
/// If true, device is considered to be "managed" by a mesh administrator
/// Clients should then limit available configuration and administrative options inside the user interface
/// Moved to SecurityConfig
///
/// NOTE: This field was marked as deprecated in the .proto file.
public var isManaged: Bool = false
///
@ -231,14 +186,14 @@ public struct Config {
public var tzdef: String = String()
///
/// If true, disable the default blinking LED (LED_PIN) behavior on the device
/// If true, disable the default blinking LED (LED_PIN) behavior on the device
public var ledHeartbeatDisabled: Bool = false
public var unknownFields = SwiftProtobuf.UnknownStorage()
///
/// Defines the device's role on the Mesh network
public enum Role: SwiftProtobuf.Enum {
public enum Role: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -256,6 +211,8 @@ public struct Config {
/// The wifi radio and the oled screen will be put to sleep.
/// This mode may still potentially have higher power usage due to it's preference in message rebroadcasting on the mesh.
case router // = 2
/// NOTE: This enum value was marked as deprecated in the .proto file
case routerClient // = 3
///
@ -356,11 +313,27 @@ public struct Config {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.DeviceConfig.Role] = [
.client,
.clientMute,
.router,
.routerClient,
.repeater,
.tracker,
.sensor,
.tak,
.clientHidden,
.lostAndFound,
.takTracker,
.routerLate,
]
}
///
/// Defines the device's behavior for how messages are rebroadcast
public enum RebroadcastMode: SwiftProtobuf.Enum {
public enum RebroadcastMode: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -421,6 +394,16 @@ public struct Config {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.DeviceConfig.RebroadcastMode] = [
.all,
.allSkipDecoding,
.localOnly,
.knownOnly,
.none,
.corePortnumsOnly,
]
}
public init() {}
@ -428,7 +411,7 @@ public struct Config {
///
/// Position Config
public struct PositionConfig {
public struct PositionConfig: 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.
@ -450,6 +433,8 @@ public struct Config {
///
/// Is GPS enabled for this node?
///
/// NOTE: This field was marked as deprecated in the .proto file.
public var gpsEnabled: Bool = false
///
@ -460,6 +445,8 @@ public struct Config {
///
/// Deprecated in favor of using smart / regular broadcast intervals as implicit attempt time
///
/// NOTE: This field was marked as deprecated in the .proto file.
public var gpsAttemptTime: UInt32 = 0
///
@ -500,7 +487,7 @@ public struct Config {
/// are always included (also time if GPS-synced)
/// NOTE: the more fields are included, the larger the message will be -
/// leading to longer airtime and a higher risk of packet loss
public enum PositionFlags: SwiftProtobuf.Enum {
public enum PositionFlags: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -590,9 +577,24 @@ public struct Config {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.PositionConfig.PositionFlags] = [
.unset,
.altitude,
.altitudeMsl,
.geoidalSeparation,
.dop,
.hvdop,
.satinview,
.seqNo,
.timestamp,
.heading,
.speed,
]
}
public enum GpsMode: SwiftProtobuf.Enum {
public enum GpsMode: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -630,6 +632,13 @@ public struct Config {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.PositionConfig.GpsMode] = [
.disabled,
.enabled,
.notPresent,
]
}
public init() {}
@ -638,13 +647,13 @@ public struct Config {
///
/// Power Config\
/// See [Power Config](/docs/settings/config/power) for additional power config details.
public struct PowerConfig {
public struct PowerConfig: 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.
///
/// Description: Will sleep everything as much as possible, for the tracker and sensor role this will also include the lora radio.
/// Description: Will sleep everything as much as possible, for the tracker and sensor role this will also include the lora radio.
/// Don't use this setting if you want to use your device with the phone apps or are using a device without a user button.
/// Technical Details: Works for ESP32 devices and NRF52 devices in the Sensor or Tracker roles
public var isPowerSaving: Bool = false
@ -698,7 +707,7 @@ public struct Config {
///
/// Network Config
public struct NetworkConfig {
public struct NetworkConfig: 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.
@ -717,7 +726,7 @@ public struct Config {
public var wifiPsk: String = String()
///
/// NTP server to use if WiFi is conneced, defaults to `0.pool.ntp.org`
/// NTP server to use if WiFi is conneced, defaults to `meshtastic.pool.ntp.org`
public var ntpServer: String = String()
///
@ -749,7 +758,7 @@ public struct Config {
public var unknownFields = SwiftProtobuf.UnknownStorage()
public enum AddressMode: SwiftProtobuf.Enum {
public enum AddressMode: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -781,11 +790,17 @@ public struct Config {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.NetworkConfig.AddressMode] = [
.dhcp,
.static,
]
}
///
/// Available flags auxiliary network protocols
public enum ProtocolFlags: SwiftProtobuf.Enum {
public enum ProtocolFlags: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -817,9 +832,15 @@ public struct Config {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.NetworkConfig.ProtocolFlags] = [
.noBroadcast,
.udpBroadcast,
]
}
public struct IpV4Config {
public struct IpV4Config: 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.
@ -852,7 +873,7 @@ public struct Config {
///
/// Display Config
public struct DisplayConfig {
public struct DisplayConfig: 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.
@ -913,7 +934,7 @@ public struct Config {
///
/// How the GPS coordinates are displayed on the OLED screen.
public enum GpsCoordinateFormat: SwiftProtobuf.Enum {
public enum GpsCoordinateFormat: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -976,11 +997,21 @@ public struct Config {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.DisplayConfig.GpsCoordinateFormat] = [
.dec,
.dms,
.utm,
.mgrs,
.olc,
.osgr,
]
}
///
/// Unit display preference
public enum DisplayUnits: SwiftProtobuf.Enum {
public enum DisplayUnits: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -1012,11 +1043,17 @@ public struct Config {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.DisplayConfig.DisplayUnits] = [
.metric,
.imperial,
]
}
///
/// Override OLED outo detect with this if it fails.
public enum OledType: SwiftProtobuf.Enum {
public enum OledType: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -1060,9 +1097,17 @@ public struct Config {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.DisplayConfig.OledType] = [
.oledAuto,
.oledSsd1306,
.oledSh1106,
.oledSh1107,
]
}
public enum DisplayMode: SwiftProtobuf.Enum {
public enum DisplayMode: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -1106,9 +1151,17 @@ public struct Config {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.DisplayConfig.DisplayMode] = [
.default,
.twocolor,
.inverted,
.color,
]
}
public enum CompassOrientation: SwiftProtobuf.Enum {
public enum CompassOrientation: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -1176,6 +1229,18 @@ public struct Config {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.DisplayConfig.CompassOrientation] = [
.degrees0,
.degrees90,
.degrees180,
.degrees270,
.degrees0Inverted,
.degrees90Inverted,
.degrees180Inverted,
.degrees270Inverted,
]
}
public init() {}
@ -1183,7 +1248,7 @@ public struct Config {
///
/// Lora Config
public struct LoRaConfig {
public struct LoRaConfig: @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.
@ -1347,7 +1412,7 @@ public struct Config {
public var unknownFields = SwiftProtobuf.UnknownStorage()
public enum RegionCode: SwiftProtobuf.Enum {
public enum RegionCode: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -1499,12 +1564,38 @@ public struct Config {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.LoRaConfig.RegionCode] = [
.unset,
.us,
.eu433,
.eu868,
.cn,
.jp,
.anz,
.kr,
.tw,
.ru,
.in,
.nz865,
.th,
.lora24,
.ua433,
.ua868,
.my433,
.my919,
.sg923,
.ph433,
.ph868,
.ph915,
]
}
///
/// Standard predefined channel settings
/// Note: these mappings must match ModemPreset Choice in the device code.
public enum ModemPreset: SwiftProtobuf.Enum {
public enum ModemPreset: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -1518,6 +1609,8 @@ public struct Config {
///
/// Very Long Range - Slow
/// Deprecated in 2.5: Works only with txco and is unusably slow
///
/// NOTE: This enum value was marked as deprecated in the .proto file
case veryLongSlow // = 2
///
@ -1581,6 +1674,19 @@ public struct Config {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.LoRaConfig.ModemPreset] = [
.longFast,
.longSlow,
.veryLongSlow,
.mediumSlow,
.mediumFast,
.shortSlow,
.shortFast,
.longModerate,
.shortTurbo,
]
}
public init() {}
@ -1588,7 +1694,7 @@ public struct Config {
fileprivate var _storage = _StorageClass.defaultInstance
}
public struct BluetoothConfig {
public struct BluetoothConfig: 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.
@ -1607,7 +1713,7 @@ public struct Config {
public var unknownFields = SwiftProtobuf.UnknownStorage()
public enum PairingMode: SwiftProtobuf.Enum {
public enum PairingMode: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -1645,12 +1751,19 @@ public struct Config {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.BluetoothConfig.PairingMode] = [
.randomPin,
.fixedPin,
.noPin,
]
}
public init() {}
}
public struct SecurityConfig {
public struct SecurityConfig: @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.
@ -1694,7 +1807,7 @@ public struct Config {
///
/// Blank config request, strictly for getting the session key
public struct SessionkeyConfig {
public struct SessionkeyConfig: 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.
@ -1707,217 +1820,6 @@ public struct Config {
public init() {}
}
#if swift(>=4.2)
extension Config.DeviceConfig.Role: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.DeviceConfig.Role] = [
.client,
.clientMute,
.router,
.routerClient,
.repeater,
.tracker,
.sensor,
.tak,
.clientHidden,
.lostAndFound,
.takTracker,
.routerLate,
]
}
extension Config.DeviceConfig.RebroadcastMode: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.DeviceConfig.RebroadcastMode] = [
.all,
.allSkipDecoding,
.localOnly,
.knownOnly,
.none,
.corePortnumsOnly,
]
}
extension Config.PositionConfig.PositionFlags: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.PositionConfig.PositionFlags] = [
.unset,
.altitude,
.altitudeMsl,
.geoidalSeparation,
.dop,
.hvdop,
.satinview,
.seqNo,
.timestamp,
.heading,
.speed,
]
}
extension Config.PositionConfig.GpsMode: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.PositionConfig.GpsMode] = [
.disabled,
.enabled,
.notPresent,
]
}
extension Config.NetworkConfig.AddressMode: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.NetworkConfig.AddressMode] = [
.dhcp,
.static,
]
}
extension Config.NetworkConfig.ProtocolFlags: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.NetworkConfig.ProtocolFlags] = [
.noBroadcast,
.udpBroadcast,
]
}
extension Config.DisplayConfig.GpsCoordinateFormat: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.DisplayConfig.GpsCoordinateFormat] = [
.dec,
.dms,
.utm,
.mgrs,
.olc,
.osgr,
]
}
extension Config.DisplayConfig.DisplayUnits: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.DisplayConfig.DisplayUnits] = [
.metric,
.imperial,
]
}
extension Config.DisplayConfig.OledType: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.DisplayConfig.OledType] = [
.oledAuto,
.oledSsd1306,
.oledSh1106,
.oledSh1107,
]
}
extension Config.DisplayConfig.DisplayMode: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.DisplayConfig.DisplayMode] = [
.default,
.twocolor,
.inverted,
.color,
]
}
extension Config.DisplayConfig.CompassOrientation: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.DisplayConfig.CompassOrientation] = [
.degrees0,
.degrees90,
.degrees180,
.degrees270,
.degrees0Inverted,
.degrees90Inverted,
.degrees180Inverted,
.degrees270Inverted,
]
}
extension Config.LoRaConfig.RegionCode: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.LoRaConfig.RegionCode] = [
.unset,
.us,
.eu433,
.eu868,
.cn,
.jp,
.anz,
.kr,
.tw,
.ru,
.in,
.nz865,
.th,
.lora24,
.ua433,
.ua868,
.my433,
.my919,
.sg923,
.ph433,
.ph868,
.ph915,
]
}
extension Config.LoRaConfig.ModemPreset: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.LoRaConfig.ModemPreset] = [
.longFast,
.longSlow,
.veryLongSlow,
.mediumSlow,
.mediumFast,
.shortSlow,
.shortFast,
.longModerate,
.shortTurbo,
]
}
extension Config.BluetoothConfig.PairingMode: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Config.BluetoothConfig.PairingMode] = [
.randomPin,
.fixedPin,
.noPin,
]
}
#endif // swift(>=4.2)
#if swift(>=5.5) && canImport(_Concurrency)
extension Config: @unchecked Sendable {}
extension Config.OneOf_PayloadVariant: @unchecked Sendable {}
extension Config.DeviceConfig: @unchecked Sendable {}
extension Config.DeviceConfig.Role: @unchecked Sendable {}
extension Config.DeviceConfig.RebroadcastMode: @unchecked Sendable {}
extension Config.PositionConfig: @unchecked Sendable {}
extension Config.PositionConfig.PositionFlags: @unchecked Sendable {}
extension Config.PositionConfig.GpsMode: @unchecked Sendable {}
extension Config.PowerConfig: @unchecked Sendable {}
extension Config.NetworkConfig: @unchecked Sendable {}
extension Config.NetworkConfig.AddressMode: @unchecked Sendable {}
extension Config.NetworkConfig.ProtocolFlags: @unchecked Sendable {}
extension Config.NetworkConfig.IpV4Config: @unchecked Sendable {}
extension Config.DisplayConfig: @unchecked Sendable {}
extension Config.DisplayConfig.GpsCoordinateFormat: @unchecked Sendable {}
extension Config.DisplayConfig.DisplayUnits: @unchecked Sendable {}
extension Config.DisplayConfig.OledType: @unchecked Sendable {}
extension Config.DisplayConfig.DisplayMode: @unchecked Sendable {}
extension Config.DisplayConfig.CompassOrientation: @unchecked Sendable {}
extension Config.LoRaConfig: @unchecked Sendable {}
extension Config.LoRaConfig.RegionCode: @unchecked Sendable {}
extension Config.LoRaConfig.ModemPreset: @unchecked Sendable {}
extension Config.BluetoothConfig: @unchecked Sendable {}
extension Config.BluetoothConfig.PairingMode: @unchecked Sendable {}
extension Config.SecurityConfig: @unchecked Sendable {}
extension Config.SessionkeyConfig: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "meshtastic"
@ -2425,7 +2327,7 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
if self.onBatteryShutdownAfterSecs != 0 {
try visitor.visitSingularUInt32Field(value: self.onBatteryShutdownAfterSecs, fieldNumber: 2)
}
if self.adcMultiplierOverride != 0 {
if self.adcMultiplierOverride.bitPattern != 0 {
try visitor.visitSingularFloatField(value: self.adcMultiplierOverride, fieldNumber: 3)
}
if self.waitBluetoothSecs != 0 {
@ -2892,7 +2794,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
if _storage._codingRate != 0 {
try visitor.visitSingularUInt32Field(value: _storage._codingRate, fieldNumber: 5)
}
if _storage._frequencyOffset != 0 {
if _storage._frequencyOffset.bitPattern != 0 {
try visitor.visitSingularFloatField(value: _storage._frequencyOffset, fieldNumber: 6)
}
if _storage._region != .unset {
@ -2916,7 +2818,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
if _storage._sx126XRxBoostedGain != false {
try visitor.visitSingularBoolField(value: _storage._sx126XRxBoostedGain, fieldNumber: 13)
}
if _storage._overrideFrequency != 0 {
if _storage._overrideFrequency.bitPattern != 0 {
try visitor.visitSingularFloatField(value: _storage._overrideFrequency, fieldNumber: 14)
}
if _storage._paFanDisabled != false {
@ -3133,8 +3035,8 @@ extension Config.SessionkeyConfig: SwiftProtobuf.Message, SwiftProtobuf._Message
public static let _protobuf_nameMap = SwiftProtobuf._NameMap()
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let _ = try decoder.nextFieldNumber() {
}
// Load everything into unknown fields
while try decoder.nextFieldNumber() != nil {}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {

View file

@ -1,5 +1,6 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/connection_status.proto
@ -7,7 +8,6 @@
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
@ -20,7 +20,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
typealias Version = _2
}
public struct DeviceConnectionStatus {
public struct DeviceConnectionStatus: 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.
@ -81,7 +81,7 @@ public struct DeviceConnectionStatus {
///
/// WiFi connection status
public struct WifiConnectionStatus {
public struct WifiConnectionStatus: 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.
@ -114,7 +114,7 @@ public struct WifiConnectionStatus {
///
/// Ethernet connection status
public struct EthernetConnectionStatus {
public struct EthernetConnectionStatus: 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.
@ -139,7 +139,7 @@ public struct EthernetConnectionStatus {
///
/// Ethernet or WiFi connection status
public struct NetworkConnectionStatus {
public struct NetworkConnectionStatus: 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.
@ -167,7 +167,7 @@ public struct NetworkConnectionStatus {
///
/// Bluetooth connection status
public struct BluetoothConnectionStatus {
public struct BluetoothConnectionStatus: 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.
@ -191,7 +191,7 @@ public struct BluetoothConnectionStatus {
///
/// Serial connection status
public struct SerialConnectionStatus {
public struct SerialConnectionStatus: 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.
@ -209,15 +209,6 @@ public struct SerialConnectionStatus {
public init() {}
}
#if swift(>=5.5) && canImport(_Concurrency)
extension DeviceConnectionStatus: @unchecked Sendable {}
extension WifiConnectionStatus: @unchecked Sendable {}
extension EthernetConnectionStatus: @unchecked Sendable {}
extension NetworkConnectionStatus: @unchecked Sendable {}
extension BluetoothConnectionStatus: @unchecked Sendable {}
extension SerialConnectionStatus: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "meshtastic"

View file

@ -1,5 +1,6 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/device_ui.proto
@ -20,7 +21,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
typealias Version = _2
}
public enum Theme: SwiftProtobuf.Enum {
public enum Theme: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -58,24 +59,18 @@ public enum Theme: SwiftProtobuf.Enum {
}
}
}
#if swift(>=4.2)
extension Theme: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Theme] = [
.dark,
.light,
.red,
]
}
#endif // swift(>=4.2)
}
///
/// Localization
public enum Language: SwiftProtobuf.Enum {
public enum Language: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -203,11 +198,6 @@ public enum Language: SwiftProtobuf.Enum {
}
}
}
#if swift(>=4.2)
extension Language: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Language] = [
.english,
@ -229,11 +219,10 @@ extension Language: CaseIterable {
.simplifiedChinese,
.traditionalChinese,
]
}
#endif // swift(>=4.2)
public struct DeviceUIConfig {
public struct DeviceUIConfig: @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.
@ -246,21 +235,21 @@ public struct DeviceUIConfig {
}
///
/// TFT display brightness 1..255
/// TFT display brightness 1..255
public var screenBrightness: UInt32 {
get {return _storage._screenBrightness}
set {_uniqueStorage()._screenBrightness = newValue}
}
///
/// Screen timeout 0..900
/// Screen timeout 0..900
public var screenTimeout: UInt32 {
get {return _storage._screenTimeout}
set {_uniqueStorage()._screenTimeout = newValue}
}
///
/// Screen/Settings lock enabled
/// Screen/Settings lock enabled
public var screenLock: Bool {
get {return _storage._screenLock}
set {_uniqueStorage()._screenLock = newValue}
@ -277,7 +266,7 @@ public struct DeviceUIConfig {
}
///
/// Color theme
/// Color theme
public var theme: Theme {
get {return _storage._theme}
set {_uniqueStorage()._theme = newValue}
@ -301,14 +290,14 @@ public struct DeviceUIConfig {
}
///
/// Localization
/// Localization
public var language: Language {
get {return _storage._language}
set {_uniqueStorage()._language = newValue}
}
///
/// Node list filter
/// Node list filter
public var nodeFilter: NodeFilter {
get {return _storage._nodeFilter ?? NodeFilter()}
set {_uniqueStorage()._nodeFilter = newValue}
@ -336,6 +325,17 @@ public struct DeviceUIConfig {
set {_uniqueStorage()._calibrationData = newValue}
}
///
/// Map related data
public var mapData: Map {
get {return _storage._mapData ?? Map()}
set {_uniqueStorage()._mapData = newValue}
}
/// Returns true if `mapData` has been explicitly set.
public var hasMapData: Bool {return _storage._mapData != nil}
/// Clears the value of `mapData`. Subsequent reads from it will return its default value.
public mutating func clearMapData() {_uniqueStorage()._mapData = nil}
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
@ -343,7 +343,7 @@ public struct DeviceUIConfig {
fileprivate var _storage = _StorageClass.defaultInstance
}
public struct NodeFilter {
public struct NodeFilter: 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.
@ -381,7 +381,7 @@ public struct NodeFilter {
public init() {}
}
public struct NodeHighlight {
public struct NodeHighlight: 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.
@ -411,13 +411,58 @@ public struct NodeHighlight {
public init() {}
}
#if swift(>=5.5) && canImport(_Concurrency)
extension Theme: @unchecked Sendable {}
extension Language: @unchecked Sendable {}
extension DeviceUIConfig: @unchecked Sendable {}
extension NodeFilter: @unchecked Sendable {}
extension NodeHighlight: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
public struct GeoPoint: 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.
///
/// Zoom level
public var zoom: Int32 = 0
///
/// Coordinate: latitude
public var latitude: Int32 = 0
///
/// Coordinate: longitude
public var longitude: Int32 = 0
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Map: 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.
///
/// Home coordinates
public var home: GeoPoint {
get {return _home ?? GeoPoint()}
set {_home = newValue}
}
/// Returns true if `home` has been explicitly set.
public var hasHome: Bool {return self._home != nil}
/// Clears the value of `home`. Subsequent reads from it will return its default value.
public mutating func clearHome() {self._home = nil}
///
/// Map tile style
public var style: String = String()
///
/// Map scroll follows GPS
public var followGps: Bool = false
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
fileprivate var _home: GeoPoint? = nil
}
// MARK: - Code below here is support for the SwiftProtobuf runtime.
@ -471,6 +516,7 @@ extension DeviceUIConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement
12: .standard(proto: "node_filter"),
13: .standard(proto: "node_highlight"),
14: .standard(proto: "calibration_data"),
15: .standard(proto: "map_data"),
]
fileprivate class _StorageClass {
@ -488,6 +534,7 @@ extension DeviceUIConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement
var _nodeFilter: NodeFilter? = nil
var _nodeHighlight: NodeHighlight? = nil
var _calibrationData: Data = Data()
var _mapData: Map? = nil
#if swift(>=5.10)
// This property is used as the initial default value for new instances of the type.
@ -516,6 +563,7 @@ extension DeviceUIConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement
_nodeFilter = source._nodeFilter
_nodeHighlight = source._nodeHighlight
_calibrationData = source._calibrationData
_mapData = source._mapData
}
}
@ -548,6 +596,7 @@ extension DeviceUIConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement
case 12: try { try decoder.decodeSingularMessageField(value: &_storage._nodeFilter) }()
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) }()
default: break
}
}
@ -602,6 +651,9 @@ extension DeviceUIConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement
if !_storage._calibrationData.isEmpty {
try visitor.visitSingularBytesField(value: _storage._calibrationData, fieldNumber: 14)
}
try { if let v = _storage._mapData {
try visitor.visitSingularMessageField(value: v, fieldNumber: 15)
} }()
}
try unknownFields.traverse(visitor: &visitor)
}
@ -625,6 +677,7 @@ extension DeviceUIConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement
if _storage._nodeFilter != rhs_storage._nodeFilter {return false}
if _storage._nodeHighlight != rhs_storage._nodeHighlight {return false}
if _storage._calibrationData != rhs_storage._calibrationData {return false}
if _storage._mapData != rhs_storage._mapData {return false}
return true
}
if !storagesAreEqual {return false}
@ -757,3 +810,95 @@ extension NodeHighlight: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa
return true
}
}
extension GeoPoint: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".GeoPoint"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "zoom"),
2: .same(proto: "latitude"),
3: .same(proto: "longitude"),
]
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.decodeSingularInt32Field(value: &self.zoom) }()
case 2: try { try decoder.decodeSingularInt32Field(value: &self.latitude) }()
case 3: try { try decoder.decodeSingularInt32Field(value: &self.longitude) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.zoom != 0 {
try visitor.visitSingularInt32Field(value: self.zoom, fieldNumber: 1)
}
if self.latitude != 0 {
try visitor.visitSingularInt32Field(value: self.latitude, fieldNumber: 2)
}
if self.longitude != 0 {
try visitor.visitSingularInt32Field(value: self.longitude, fieldNumber: 3)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: GeoPoint, rhs: GeoPoint) -> Bool {
if lhs.zoom != rhs.zoom {return false}
if lhs.latitude != rhs.latitude {return false}
if lhs.longitude != rhs.longitude {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Map: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".Map"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "home"),
2: .same(proto: "style"),
3: .standard(proto: "follow_gps"),
]
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.decodeSingularMessageField(value: &self._home) }()
case 2: try { try decoder.decodeSingularStringField(value: &self.style) }()
case 3: try { try decoder.decodeSingularBoolField(value: &self.followGps) }()
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
try { if let v = self._home {
try visitor.visitSingularMessageField(value: v, fieldNumber: 1)
} }()
if !self.style.isEmpty {
try visitor.visitSingularStringField(value: self.style, fieldNumber: 2)
}
if self.followGps != false {
try visitor.visitSingularBoolField(value: self.followGps, fieldNumber: 3)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Map, rhs: Map) -> Bool {
if lhs._home != rhs._home {return false}
if lhs.style != rhs.style {return false}
if lhs.followGps != rhs.followGps {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}

View file

@ -1,5 +1,6 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/deviceonly.proto
@ -22,7 +23,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
///
/// Position with static location information only for NodeDBLite
public struct PositionLite {
public struct PositionLite: 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.
@ -57,13 +58,15 @@ public struct PositionLite {
public init() {}
}
public struct UserLite {
public struct UserLite: @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.
///
/// This is the addr of the radio.
///
/// NOTE: This field was marked as deprecated in the .proto file.
public var macaddr: Data = Data()
///
@ -102,7 +105,7 @@ public struct UserLite {
public init() {}
}
public struct NodeInfoLite {
public struct NodeInfoLite: @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.
@ -205,7 +208,7 @@ public struct NodeInfoLite {
}
///
/// Last byte of the node number of the node that should be used as the next hop to reach this node.
/// Last byte of the node number of the node that should be used as the next hop to reach this node.
public var nextHop: UInt32 {
get {return _storage._nextHop}
set {_uniqueStorage()._nextHop = newValue}
@ -224,7 +227,7 @@ public struct NodeInfoLite {
/// FIXME, since we write this each time we enter deep sleep (and have infinite
/// flash) it would be better to use some sort of append only data structure for
/// the receive queue and use the preferences store for the other stuff
public struct DeviceState {
public struct DeviceState: @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.
@ -284,13 +287,18 @@ public struct DeviceState {
/// Used only during development.
/// Indicates developer is testing and changes should never be saved to flash.
/// Deprecated in 2.3.1
///
/// NOTE: This field was marked as deprecated in the .proto file.
public var noSave: Bool {
get {return _storage._noSave}
set {_uniqueStorage()._noSave = newValue}
}
///
/// Some GPS receivers seem to have bogus settings from the factory, so we always do one factory reset.
/// Previously used to manage GPS factory resets.
/// Deprecated in 2.5.23
///
/// NOTE: This field was marked as deprecated in the .proto file.
public var didGpsReset: Bool {
get {return _storage._didGpsReset}
set {_uniqueStorage()._didGpsReset = newValue}
@ -316,13 +324,6 @@ public struct DeviceState {
set {_uniqueStorage()._nodeRemoteHardwarePins = newValue}
}
///
/// New lite version of NodeDB to decrease memory footprint
public var nodeDbLite: [NodeInfoLite] {
get {return _storage._nodeDbLite}
set {_uniqueStorage()._nodeDbLite = newValue}
}
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
@ -330,9 +331,29 @@ public struct DeviceState {
fileprivate var _storage = _StorageClass.defaultInstance
}
public struct NodeDatabase: 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.
///
/// A version integer used to invalidate old save files when we make
/// incompatible changes This integer is set at build time and is private to
/// NodeDB.cpp in the device code.
public var version: UInt32 = 0
///
/// New lite version of NodeDB to decrease memory footprint
public var nodes: [NodeInfoLite] = []
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
///
/// The on-disk saved channels
public struct ChannelFile {
public struct ChannelFile: 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.
@ -352,13 +373,74 @@ public struct ChannelFile {
public init() {}
}
#if swift(>=5.5) && canImport(_Concurrency)
extension PositionLite: @unchecked Sendable {}
extension UserLite: @unchecked Sendable {}
extension NodeInfoLite: @unchecked Sendable {}
extension DeviceState: @unchecked Sendable {}
extension ChannelFile: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
///
/// The on-disk backup of the node's preferences
public struct BackupPreferences: 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.
///
/// The version of the backup
public var version: UInt32 = 0
///
/// The timestamp of the backup (if node has time)
public var timestamp: UInt32 = 0
///
/// The node's configuration
public var config: LocalConfig {
get {return _config ?? LocalConfig()}
set {_config = newValue}
}
/// Returns true if `config` has been explicitly set.
public var hasConfig: Bool {return self._config != nil}
/// Clears the value of `config`. Subsequent reads from it will return its default value.
public mutating func clearConfig() {self._config = nil}
///
/// The node's module configuration
public var moduleConfig: LocalModuleConfig {
get {return _moduleConfig ?? LocalModuleConfig()}
set {_moduleConfig = newValue}
}
/// Returns true if `moduleConfig` has been explicitly set.
public var hasModuleConfig: Bool {return self._moduleConfig != nil}
/// Clears the value of `moduleConfig`. Subsequent reads from it will return its default value.
public mutating func clearModuleConfig() {self._moduleConfig = nil}
///
/// The node's channels
public var channels: ChannelFile {
get {return _channels ?? ChannelFile()}
set {_channels = newValue}
}
/// Returns true if `channels` has been explicitly set.
public var hasChannels: Bool {return self._channels != nil}
/// Clears the value of `channels`. Subsequent reads from it will return its default value.
public mutating func clearChannels() {self._channels = nil}
///
/// The node's user (owner) information
public var owner: User {
get {return _owner ?? User()}
set {_owner = newValue}
}
/// Returns true if `owner` has been explicitly set.
public var hasOwner: Bool {return self._owner != nil}
/// Clears the value of `owner`. Subsequent reads from it will return its default value.
public mutating func clearOwner() {self._owner = nil}
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
fileprivate var _config: LocalConfig? = nil
fileprivate var _moduleConfig: LocalModuleConfig? = nil
fileprivate var _channels: ChannelFile? = nil
fileprivate var _owner: User? = nil
}
// MARK: - Code below here is support for the SwiftProtobuf runtime.
@ -595,7 +677,7 @@ extension NodeInfoLite: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
try { if let v = _storage._position {
try visitor.visitSingularMessageField(value: v, fieldNumber: 3)
} }()
if _storage._snr != 0 {
if _storage._snr.bitPattern != 0 {
try visitor.visitSingularFloatField(value: _storage._snr, fieldNumber: 4)
}
if _storage._lastHeard != 0 {
@ -664,7 +746,6 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
11: .standard(proto: "did_gps_reset"),
12: .standard(proto: "rx_waypoint"),
13: .standard(proto: "node_remote_hardware_pins"),
14: .standard(proto: "node_db_lite"),
]
fileprivate class _StorageClass {
@ -677,7 +758,6 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
var _didGpsReset: Bool = false
var _rxWaypoint: MeshPacket? = nil
var _nodeRemoteHardwarePins: [NodeRemoteHardwarePin] = []
var _nodeDbLite: [NodeInfoLite] = []
#if swift(>=5.10)
// This property is used as the initial default value for new instances of the type.
@ -701,7 +781,6 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
_didGpsReset = source._didGpsReset
_rxWaypoint = source._rxWaypoint
_nodeRemoteHardwarePins = source._nodeRemoteHardwarePins
_nodeDbLite = source._nodeDbLite
}
}
@ -729,7 +808,6 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
case 11: try { try decoder.decodeSingularBoolField(value: &_storage._didGpsReset) }()
case 12: try { try decoder.decodeSingularMessageField(value: &_storage._rxWaypoint) }()
case 13: try { try decoder.decodeRepeatedMessageField(value: &_storage._nodeRemoteHardwarePins) }()
case 14: try { try decoder.decodeRepeatedMessageField(value: &_storage._nodeDbLite) }()
default: break
}
}
@ -769,9 +847,6 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
if !_storage._nodeRemoteHardwarePins.isEmpty {
try visitor.visitRepeatedMessageField(value: _storage._nodeRemoteHardwarePins, fieldNumber: 13)
}
if !_storage._nodeDbLite.isEmpty {
try visitor.visitRepeatedMessageField(value: _storage._nodeDbLite, fieldNumber: 14)
}
}
try unknownFields.traverse(visitor: &visitor)
}
@ -790,7 +865,6 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
if _storage._didGpsReset != rhs_storage._didGpsReset {return false}
if _storage._rxWaypoint != rhs_storage._rxWaypoint {return false}
if _storage._nodeRemoteHardwarePins != rhs_storage._nodeRemoteHardwarePins {return false}
if _storage._nodeDbLite != rhs_storage._nodeDbLite {return false}
return true
}
if !storagesAreEqual {return false}
@ -800,6 +874,44 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
}
}
extension NodeDatabase: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".NodeDatabase"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "version"),
2: .same(proto: "nodes"),
]
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.decodeSingularUInt32Field(value: &self.version) }()
case 2: try { try decoder.decodeRepeatedMessageField(value: &self.nodes) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.version != 0 {
try visitor.visitSingularUInt32Field(value: self.version, fieldNumber: 1)
}
if !self.nodes.isEmpty {
try visitor.visitRepeatedMessageField(value: self.nodes, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: NodeDatabase, rhs: NodeDatabase) -> Bool {
if lhs.version != rhs.version {return false}
if lhs.nodes != rhs.nodes {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension ChannelFile: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".ChannelFile"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
@ -837,3 +949,69 @@ extension ChannelFile: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
return true
}
}
extension BackupPreferences: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".BackupPreferences"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "version"),
2: .same(proto: "timestamp"),
3: .same(proto: "config"),
4: .standard(proto: "module_config"),
5: .same(proto: "channels"),
6: .same(proto: "owner"),
]
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.decodeSingularUInt32Field(value: &self.version) }()
case 2: try { try decoder.decodeSingularFixed32Field(value: &self.timestamp) }()
case 3: try { try decoder.decodeSingularMessageField(value: &self._config) }()
case 4: try { try decoder.decodeSingularMessageField(value: &self._moduleConfig) }()
case 5: try { try decoder.decodeSingularMessageField(value: &self._channels) }()
case 6: try { try decoder.decodeSingularMessageField(value: &self._owner) }()
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.version != 0 {
try visitor.visitSingularUInt32Field(value: self.version, fieldNumber: 1)
}
if self.timestamp != 0 {
try visitor.visitSingularFixed32Field(value: self.timestamp, fieldNumber: 2)
}
try { if let v = self._config {
try visitor.visitSingularMessageField(value: v, fieldNumber: 3)
} }()
try { if let v = self._moduleConfig {
try visitor.visitSingularMessageField(value: v, fieldNumber: 4)
} }()
try { if let v = self._channels {
try visitor.visitSingularMessageField(value: v, fieldNumber: 5)
} }()
try { if let v = self._owner {
try visitor.visitSingularMessageField(value: v, fieldNumber: 6)
} }()
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: BackupPreferences, rhs: BackupPreferences) -> Bool {
if lhs.version != rhs.version {return false}
if lhs.timestamp != rhs.timestamp {return false}
if lhs._config != rhs._config {return false}
if lhs._moduleConfig != rhs._moduleConfig {return false}
if lhs._channels != rhs._channels {return false}
if lhs._owner != rhs._owner {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}

View file

@ -0,0 +1,328 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/interdevice.proto
//
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
// was generated by a version of the `protoc` Swift plug-in that is
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that you are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
public enum MessageType: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
case ack // = 0
/// in ms
case collectInterval // = 160
/// duration ms
case beepOn // = 161
/// cancel prematurely
case beepOff // = 162
case shutdown // = 163
case powerOn // = 164
case scd41Temp // = 176
case scd41Humidity // = 177
case scd41Co2 // = 178
case aht20Temp // = 179
case aht20Humidity // = 180
case tvocIndex // = 181
case UNRECOGNIZED(Int)
public init() {
self = .ack
}
public init?(rawValue: Int) {
switch rawValue {
case 0: self = .ack
case 160: self = .collectInterval
case 161: self = .beepOn
case 162: self = .beepOff
case 163: self = .shutdown
case 164: self = .powerOn
case 176: self = .scd41Temp
case 177: self = .scd41Humidity
case 178: self = .scd41Co2
case 179: self = .aht20Temp
case 180: self = .aht20Humidity
case 181: self = .tvocIndex
default: self = .UNRECOGNIZED(rawValue)
}
}
public var rawValue: Int {
switch self {
case .ack: return 0
case .collectInterval: return 160
case .beepOn: return 161
case .beepOff: return 162
case .shutdown: return 163
case .powerOn: return 164
case .scd41Temp: return 176
case .scd41Humidity: return 177
case .scd41Co2: return 178
case .aht20Temp: return 179
case .aht20Humidity: return 180
case .tvocIndex: return 181
case .UNRECOGNIZED(let i): return i
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [MessageType] = [
.ack,
.collectInterval,
.beepOn,
.beepOff,
.shutdown,
.powerOn,
.scd41Temp,
.scd41Humidity,
.scd41Co2,
.aht20Temp,
.aht20Humidity,
.tvocIndex,
]
}
public struct SensorData: 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.
/// The message type
public var type: MessageType = .ack
/// The sensor data, either as a float or an uint32
public var data: SensorData.OneOf_Data? = nil
public var floatValue: Float {
get {
if case .floatValue(let v)? = data {return v}
return 0
}
set {data = .floatValue(newValue)}
}
public var uint32Value: UInt32 {
get {
if case .uint32Value(let v)? = data {return v}
return 0
}
set {data = .uint32Value(newValue)}
}
public var unknownFields = SwiftProtobuf.UnknownStorage()
/// The sensor data, either as a float or an uint32
public enum OneOf_Data: Equatable, Sendable {
case floatValue(Float)
case uint32Value(UInt32)
}
public init() {}
}
public struct InterdeviceMessage: 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.
/// The message data
public var data: InterdeviceMessage.OneOf_Data? = nil
public var nmea: String {
get {
if case .nmea(let v)? = data {return v}
return String()
}
set {data = .nmea(newValue)}
}
public var sensor: SensorData {
get {
if case .sensor(let v)? = data {return v}
return SensorData()
}
set {data = .sensor(newValue)}
}
public var unknownFields = SwiftProtobuf.UnknownStorage()
/// The message data
public enum OneOf_Data: Equatable, Sendable {
case nmea(String)
case sensor(SensorData)
}
public init() {}
}
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "meshtastic"
extension MessageType: SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "ACK"),
160: .same(proto: "COLLECT_INTERVAL"),
161: .same(proto: "BEEP_ON"),
162: .same(proto: "BEEP_OFF"),
163: .same(proto: "SHUTDOWN"),
164: .same(proto: "POWER_ON"),
176: .same(proto: "SCD41_TEMP"),
177: .same(proto: "SCD41_HUMIDITY"),
178: .same(proto: "SCD41_CO2"),
179: .same(proto: "AHT20_TEMP"),
180: .same(proto: "AHT20_HUMIDITY"),
181: .same(proto: "TVOC_INDEX"),
]
}
extension SensorData: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".SensorData"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "type"),
2: .standard(proto: "float_value"),
3: .standard(proto: "uint32_value"),
]
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.decodeSingularEnumField(value: &self.type) }()
case 2: try {
var v: Float?
try decoder.decodeSingularFloatField(value: &v)
if let v = v {
if self.data != nil {try decoder.handleConflictingOneOf()}
self.data = .floatValue(v)
}
}()
case 3: try {
var v: UInt32?
try decoder.decodeSingularUInt32Field(value: &v)
if let v = v {
if self.data != nil {try decoder.handleConflictingOneOf()}
self.data = .uint32Value(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.type != .ack {
try visitor.visitSingularEnumField(value: self.type, fieldNumber: 1)
}
switch self.data {
case .floatValue?: try {
guard case .floatValue(let v)? = self.data else { preconditionFailure() }
try visitor.visitSingularFloatField(value: v, fieldNumber: 2)
}()
case .uint32Value?: try {
guard case .uint32Value(let v)? = self.data else { preconditionFailure() }
try visitor.visitSingularUInt32Field(value: v, fieldNumber: 3)
}()
case nil: break
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: SensorData, rhs: SensorData) -> Bool {
if lhs.type != rhs.type {return false}
if lhs.data != rhs.data {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension InterdeviceMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".InterdeviceMessage"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "nmea"),
2: .same(proto: "sensor"),
]
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 {
var v: String?
try decoder.decodeSingularStringField(value: &v)
if let v = v {
if self.data != nil {try decoder.handleConflictingOneOf()}
self.data = .nmea(v)
}
}()
case 2: try {
var v: SensorData?
var hadOneofValue = false
if let current = self.data {
hadOneofValue = true
if case .sensor(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
self.data = .sensor(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
switch self.data {
case .nmea?: try {
guard case .nmea(let v)? = self.data else { preconditionFailure() }
try visitor.visitSingularStringField(value: v, fieldNumber: 1)
}()
case .sensor?: try {
guard case .sensor(let v)? = self.data else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
}()
case nil: break
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: InterdeviceMessage, rhs: InterdeviceMessage) -> Bool {
if lhs.data != rhs.data {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}

View file

@ -1,5 +1,6 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/localonly.proto
@ -7,7 +8,6 @@
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
@ -20,7 +20,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
typealias Version = _2
}
public struct LocalConfig {
public struct LocalConfig: @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.
@ -129,7 +129,7 @@ public struct LocalConfig {
fileprivate var _storage = _StorageClass.defaultInstance
}
public struct LocalModuleConfig {
public struct LocalModuleConfig: @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.
@ -293,11 +293,6 @@ public struct LocalModuleConfig {
fileprivate var _storage = _StorageClass.defaultInstance
}
#if swift(>=5.5) && canImport(_Concurrency)
extension LocalConfig: @unchecked Sendable {}
extension LocalModuleConfig: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "meshtastic"

View file

@ -1,5 +1,6 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/mesh.proto
@ -25,7 +26,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
/// bin/build-all.sh script.
/// Because they will be used to find firmware filenames in the android app for OTA updates.
/// To match the old style filenames, _ is converted to -, p is converted to .
public enum HardwareModel: SwiftProtobuf.Enum {
public enum HardwareModel: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -290,7 +291,7 @@ public enum HardwareModel: SwiftProtobuf.Enum {
case cdebyteEoraS3 // = 61
///
/// TWC_MESH_V4
/// TWC_MESH_V4
/// Adafruit NRF52840 feather express with SX1262, SSD1306 OLED and NEO6M GPS
case twcMeshV4 // = 62
@ -402,6 +403,10 @@ public enum HardwareModel: SwiftProtobuf.Enum {
/// https://www.loraitalia.it
case meshlink // = 87
///
/// Seeed XIAO nRF52840 + Wio SX1262 kit
case xiaoNrf52Kit // = 88
///
/// ------------------------------------------------------------------------------------------------------------------------------------------
/// 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.
@ -503,6 +508,7 @@ public enum HardwareModel: SwiftProtobuf.Enum {
case 85: self = .routastic
case 86: self = .meshTab
case 87: self = .meshlink
case 88: self = .xiaoNrf52Kit
case 255: self = .privateHw
default: self = .UNRECOGNIZED(rawValue)
}
@ -598,16 +604,12 @@ public enum HardwareModel: SwiftProtobuf.Enum {
case .routastic: return 85
case .meshTab: return 86
case .meshlink: return 87
case .xiaoNrf52Kit: return 88
case .privateHw: return 255
case .UNRECOGNIZED(let i): return i
}
}
}
#if swift(>=4.2)
extension HardwareModel: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [HardwareModel] = [
.unset,
@ -698,15 +700,15 @@ extension HardwareModel: CaseIterable {
.routastic,
.meshTab,
.meshlink,
.xiaoNrf52Kit,
.privateHw,
]
}
#endif // swift(>=4.2)
}
///
/// Shared constants between device and phone
public enum Constants: SwiftProtobuf.Enum {
public enum Constants: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -741,26 +743,20 @@ public enum Constants: SwiftProtobuf.Enum {
}
}
}
#if swift(>=4.2)
extension Constants: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Constants] = [
.zero,
.dataPayloadLen,
]
}
#endif // swift(>=4.2)
}
///
/// Error codes for critical errors
/// The device might report these fault codes on the screen.
/// If you encounter a fault code, please post on the meshtastic.discourse.group
/// and we'll try to help.
public enum CriticalErrorCode: SwiftProtobuf.Enum {
public enum CriticalErrorCode: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -869,11 +865,6 @@ public enum CriticalErrorCode: SwiftProtobuf.Enum {
}
}
}
#if swift(>=4.2)
extension CriticalErrorCode: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [CriticalErrorCode] = [
.none,
@ -891,15 +882,14 @@ extension CriticalErrorCode: CaseIterable {
.flashCorruptionRecoverable,
.flashCorruptionUnrecoverable,
]
}
#endif // swift(>=4.2)
}
///
/// Enum for modules excluded from a device's configuration.
/// Each value represents a ModuleConfigType that can be toggled as excluded
/// by setting its corresponding bit in the `excluded_modules` bitmask field.
public enum ExcludedModules: SwiftProtobuf.Enum {
public enum ExcludedModules: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -1003,11 +993,6 @@ public enum ExcludedModules: SwiftProtobuf.Enum {
}
}
}
#if swift(>=4.2)
extension ExcludedModules: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [ExcludedModules] = [
.excludedNone,
@ -1025,13 +1010,12 @@ extension ExcludedModules: CaseIterable {
.detectionsensorConfig,
.paxcounterConfig,
]
}
#endif // swift(>=4.2)
}
///
/// A GPS Position
public struct Position {
public struct Position: @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.
@ -1248,7 +1232,7 @@ public struct Position {
///
/// How the location was acquired: manual, onboard GPS, external (EUD) GPS
public enum LocSource: SwiftProtobuf.Enum {
public enum LocSource: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -1292,12 +1276,20 @@ public struct Position {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Position.LocSource] = [
.locUnset,
.locManual,
.locInternal,
.locExternal,
]
}
///
/// How the altitude was acquired: manual, GPS int/ext, etc
/// Default: same as location_source if present
public enum AltSource: SwiftProtobuf.Enum {
public enum AltSource: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -1347,6 +1339,15 @@ public struct Position {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Position.AltSource] = [
.altUnset,
.altManual,
.altInternal,
.altExternal,
.altBarometric,
]
}
public init() {}
@ -1354,31 +1355,6 @@ public struct Position {
fileprivate var _storage = _StorageClass.defaultInstance
}
#if swift(>=4.2)
extension Position.LocSource: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Position.LocSource] = [
.locUnset,
.locManual,
.locInternal,
.locExternal,
]
}
extension Position.AltSource: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Position.AltSource] = [
.altUnset,
.altManual,
.altInternal,
.altExternal,
.altBarometric,
]
}
#endif // swift(>=4.2)
///
/// Broadcast when a newly powered mesh node wants to find a node num it can use
/// Sent from the phone over bluetooth to set the user id for the owner of this node.
@ -1400,7 +1376,7 @@ extension Position.AltSource: CaseIterable {
/// A few nodenums are reserved and will never be requested:
/// 0xff - broadcast
/// 0 through 3 - for future use
public struct User {
public struct User: @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.
@ -1425,6 +1401,8 @@ public struct User {
/// Deprecated in Meshtastic 2.1.x
/// This is the addr of the radio.
/// Not populated by the phone, but added by the esp32 when broadcasting
///
/// NOTE: This field was marked as deprecated in the .proto file.
public var macaddr: Data = Data()
///
@ -1456,7 +1434,7 @@ public struct User {
///
/// A message used in a traceroute
public struct RouteDiscovery {
public struct RouteDiscovery: 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.
@ -1484,7 +1462,7 @@ public struct RouteDiscovery {
///
/// A Routing control Data packet handled by the routing module
public struct Routing {
public struct Routing: 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.
@ -1524,7 +1502,7 @@ public struct Routing {
public var unknownFields = SwiftProtobuf.UnknownStorage()
public enum OneOf_Variant: Equatable {
public enum OneOf_Variant: Equatable, Sendable {
///
/// A route request going from the requester
case routeRequest(RouteDiscovery)
@ -1536,34 +1514,12 @@ public struct Routing {
/// in addition to ack.fail_id to provide details on the type of failure).
case errorReason(Routing.Error)
#if !swift(>=4.1)
public static func ==(lhs: Routing.OneOf_Variant, rhs: Routing.OneOf_Variant) -> Bool {
// 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 (lhs, rhs) {
case (.routeRequest, .routeRequest): return {
guard case .routeRequest(let l) = lhs, case .routeRequest(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.routeReply, .routeReply): return {
guard case .routeReply(let l) = lhs, case .routeReply(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.errorReason, .errorReason): return {
guard case .errorReason(let l) = lhs, case .errorReason(let r) = rhs else { preconditionFailure() }
return l == r
}()
default: return false
}
}
#endif
}
///
/// A failure in delivering a message (usually used for routing control messages, but might be provided in addition to ack.fail_id to provide
/// details on the type of failure).
public enum Error: SwiftProtobuf.Enum {
public enum Error: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -1681,42 +1637,36 @@ public struct Routing {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Routing.Error] = [
.none,
.noRoute,
.gotNak,
.timeout,
.noInterface,
.maxRetransmit,
.noChannel,
.tooLarge,
.noResponse,
.dutyCycleLimit,
.badRequest,
.notAuthorized,
.pkiFailed,
.pkiUnknownPubkey,
.adminBadSessionKey,
.adminPublicKeyUnauthorized,
]
}
public init() {}
}
#if swift(>=4.2)
extension Routing.Error: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Routing.Error] = [
.none,
.noRoute,
.gotNak,
.timeout,
.noInterface,
.maxRetransmit,
.noChannel,
.tooLarge,
.noResponse,
.dutyCycleLimit,
.badRequest,
.notAuthorized,
.pkiFailed,
.pkiUnknownPubkey,
.adminBadSessionKey,
.adminPublicKeyUnauthorized,
]
}
#endif // swift(>=4.2)
///
/// (Formerly called SubPacket)
/// The payload portion fo a packet, this is the actual bytes that are sent
/// inside a radio packet (because from/to are broken out by the comms library)
public struct DataMessage {
public struct DataMessage: @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.
@ -1783,7 +1733,7 @@ public struct DataMessage {
///
/// Waypoint message, used to share arbitrary locations across the mesh
public struct Waypoint {
public struct Waypoint: 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.
@ -1845,7 +1795,7 @@ public struct Waypoint {
///
/// This message will be proxied over the PhoneAPI for the client to deliver to the MQTT server
public struct MqttClientProxyMessage {
public struct MqttClientProxyMessage: @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.
@ -1886,7 +1836,7 @@ public struct MqttClientProxyMessage {
///
/// The actual service envelope payload or text for mqtt pub / sub
public enum OneOf_PayloadVariant: Equatable {
public enum OneOf_PayloadVariant: Equatable, @unchecked Sendable {
///
/// Bytes
case data(Data)
@ -1894,24 +1844,6 @@ public struct MqttClientProxyMessage {
/// Text
case text(String)
#if !swift(>=4.1)
public static func ==(lhs: MqttClientProxyMessage.OneOf_PayloadVariant, rhs: MqttClientProxyMessage.OneOf_PayloadVariant) -> Bool {
// 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 (lhs, rhs) {
case (.data, .data): return {
guard case .data(let l) = lhs, case .data(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.text, .text): return {
guard case .text(let l) = lhs, case .text(let r) = rhs else { preconditionFailure() }
return l == r
}()
default: return false
}
}
#endif
}
public init() {}
@ -1921,7 +1853,7 @@ public struct MqttClientProxyMessage {
/// A packet envelope sent/received over the mesh
/// only payload_variant is sent in the payload portion of the LORA packet.
/// The other fields are either not sent at all, or sent in the special 16 byte LORA header.
public struct MeshPacket {
public struct MeshPacket: @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.
@ -2055,6 +1987,8 @@ public struct MeshPacket {
///
/// Describe if this message is delayed
///
/// NOTE: This field was marked as deprecated in the .proto file.
public var delayed: MeshPacket.Delayed {
get {return _storage._delayed}
set {_uniqueStorage()._delayed = newValue}
@ -2090,7 +2024,7 @@ public struct MeshPacket {
}
///
/// Last byte of the node number of the node that should be used as the next hop in routing.
/// Last byte of the node number of the node that should be used as the next hop in routing.
/// Set by the firmware internally, clients are not supposed to set this.
public var nextHop: UInt32 {
get {return _storage._nextHop}
@ -2116,7 +2050,7 @@ public struct MeshPacket {
public var unknownFields = SwiftProtobuf.UnknownStorage()
public enum OneOf_PayloadVariant: Equatable {
public enum OneOf_PayloadVariant: Equatable, @unchecked Sendable {
///
/// TODO: REPLACE
case decoded(DataMessage)
@ -2124,24 +2058,6 @@ public struct MeshPacket {
/// TODO: REPLACE
case encrypted(Data)
#if !swift(>=4.1)
public static func ==(lhs: MeshPacket.OneOf_PayloadVariant, rhs: MeshPacket.OneOf_PayloadVariant) -> Bool {
// 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 (lhs, rhs) {
case (.decoded, .decoded): return {
guard case .decoded(let l) = lhs, case .decoded(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.encrypted, .encrypted): return {
guard case .encrypted(let l) = lhs, case .encrypted(let r) = rhs else { preconditionFailure() }
return l == r
}()
default: return false
}
}
#endif
}
///
@ -2163,7 +2079,7 @@ public struct MeshPacket {
/// So I bit the bullet and implemented a new (internal - not sent over the air)
/// field in MeshPacket called 'priority'.
/// And the transmission queue in the router object is now a priority queue.
public enum Priority: SwiftProtobuf.Enum {
public enum Priority: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -2247,11 +2163,25 @@ public struct MeshPacket {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [MeshPacket.Priority] = [
.unset,
.min,
.background,
.default,
.reliable,
.response,
.high,
.alert,
.ack,
.max,
]
}
///
/// Identify if this is a delayed packet
public enum Delayed: SwiftProtobuf.Enum {
public enum Delayed: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -2289,6 +2219,13 @@ public struct MeshPacket {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [MeshPacket.Delayed] = [
.noDelay,
.broadcast,
.direct,
]
}
public init() {}
@ -2296,35 +2233,6 @@ public struct MeshPacket {
fileprivate var _storage = _StorageClass.defaultInstance
}
#if swift(>=4.2)
extension MeshPacket.Priority: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [MeshPacket.Priority] = [
.unset,
.min,
.background,
.default,
.reliable,
.response,
.high,
.alert,
.ack,
.max,
]
}
extension MeshPacket.Delayed: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [MeshPacket.Delayed] = [
.noDelay,
.broadcast,
.direct,
]
}
#endif // swift(>=4.2)
///
/// The bluetooth to device link:
/// Old BTLE protocol docs from TODO, merge in above and make real docs...
@ -2342,7 +2250,7 @@ extension MeshPacket.Delayed: CaseIterable {
/// level etc) SET_CONFIG (switches device to a new set of radio params and
/// preshared key, drops all existing nodes, force our node to rejoin this new group)
/// Full information about a node on the mesh
public struct NodeInfo {
public struct NodeInfo: @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.
@ -2455,7 +2363,7 @@ public struct NodeInfo {
/// Unique local debugging info for this node
/// Note: we don't include position or the user info, because that will come in the
/// Sent to the phone in response to WantNodes.
public struct MyNodeInfo {
public struct MyNodeInfo: @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.
@ -2494,7 +2402,7 @@ public struct MyNodeInfo {
/// on the message it is assumed to be a continuation of the previously sent message.
/// This allows the device code to use fixed maxlen 64 byte strings for messages,
/// and then extend as needed by emitting multiple records.
public struct LogRecord {
public struct LogRecord: 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.
@ -2519,7 +2427,7 @@ public struct LogRecord {
///
/// Log levels, chosen to match python logging conventions.
public enum Level: SwiftProtobuf.Enum {
public enum Level: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -2581,29 +2489,23 @@ public struct LogRecord {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [LogRecord.Level] = [
.unset,
.critical,
.error,
.warning,
.info,
.debug,
.trace,
]
}
public init() {}
}
#if swift(>=4.2)
extension LogRecord.Level: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [LogRecord.Level] = [
.unset,
.critical,
.error,
.warning,
.info,
.debug,
.trace,
]
}
#endif // swift(>=4.2)
public struct QueueStatus {
public struct QueueStatus: 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.
@ -2630,7 +2532,7 @@ public struct QueueStatus {
/// It will support READ and NOTIFY. When a new packet arrives the device will BLE notify?
/// It will sit in that descriptor until consumed by the phone,
/// at which point the next item in the FIFO will be populated.
public struct FromRadio {
public struct FromRadio: 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.
@ -2816,7 +2718,7 @@ public struct FromRadio {
///
/// Log levels, chosen to match python logging conventions.
public enum OneOf_PayloadVariant: Equatable {
public enum OneOf_PayloadVariant: Equatable, Sendable {
///
/// Log levels, chosen to match python logging conventions.
case packet(MeshPacket)
@ -2874,80 +2776,6 @@ public struct FromRadio {
/// Persistent data for device-ui
case deviceuiConfig(DeviceUIConfig)
#if !swift(>=4.1)
public static func ==(lhs: FromRadio.OneOf_PayloadVariant, rhs: FromRadio.OneOf_PayloadVariant) -> Bool {
// 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 (lhs, rhs) {
case (.packet, .packet): return {
guard case .packet(let l) = lhs, case .packet(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.myInfo, .myInfo): return {
guard case .myInfo(let l) = lhs, case .myInfo(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.nodeInfo, .nodeInfo): return {
guard case .nodeInfo(let l) = lhs, case .nodeInfo(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.config, .config): return {
guard case .config(let l) = lhs, case .config(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.logRecord, .logRecord): return {
guard case .logRecord(let l) = lhs, case .logRecord(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.configCompleteID, .configCompleteID): return {
guard case .configCompleteID(let l) = lhs, case .configCompleteID(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.rebooted, .rebooted): return {
guard case .rebooted(let l) = lhs, case .rebooted(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.moduleConfig, .moduleConfig): return {
guard case .moduleConfig(let l) = lhs, case .moduleConfig(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.channel, .channel): return {
guard case .channel(let l) = lhs, case .channel(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.queueStatus, .queueStatus): return {
guard case .queueStatus(let l) = lhs, case .queueStatus(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.xmodemPacket, .xmodemPacket): return {
guard case .xmodemPacket(let l) = lhs, case .xmodemPacket(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.metadata, .metadata): return {
guard case .metadata(let l) = lhs, case .metadata(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.mqttClientProxyMessage, .mqttClientProxyMessage): return {
guard case .mqttClientProxyMessage(let l) = lhs, case .mqttClientProxyMessage(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.fileInfo, .fileInfo): return {
guard case .fileInfo(let l) = lhs, case .fileInfo(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.clientNotification, .clientNotification): return {
guard case .clientNotification(let l) = lhs, case .clientNotification(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.deviceuiConfig, .deviceuiConfig): return {
guard case .deviceuiConfig(let l) = lhs, case .deviceuiConfig(let r) = rhs else { preconditionFailure() }
return l == r
}()
default: return false
}
}
#endif
}
public init() {}
@ -2958,7 +2786,7 @@ public struct FromRadio {
/// To be used for important messages that should to be displayed to the user
/// in the form of push notifications or validation messages when saving
/// invalid configuration.
public struct ClientNotification {
public struct ClientNotification: 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.
@ -2995,7 +2823,7 @@ public struct ClientNotification {
///
/// Individual File info for the device
public struct FileInfo {
public struct FileInfo: 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.
@ -3016,7 +2844,7 @@ public struct FileInfo {
///
/// Packets/commands to the radio will be written (reliably) to the toRadio characteristic.
/// Once the write completes the phone can assume it is handled.
public struct ToRadio {
public struct ToRadio: 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.
@ -3096,7 +2924,7 @@ public struct ToRadio {
///
/// Log levels, chosen to match python logging conventions.
public enum OneOf_PayloadVariant: Equatable {
public enum OneOf_PayloadVariant: Equatable, Sendable {
///
/// Send this packet on the mesh
case packet(MeshPacket)
@ -3123,40 +2951,6 @@ public struct ToRadio {
/// Heartbeat message (used to keep the device connection awake on serial)
case heartbeat(Heartbeat)
#if !swift(>=4.1)
public static func ==(lhs: ToRadio.OneOf_PayloadVariant, rhs: ToRadio.OneOf_PayloadVariant) -> Bool {
// 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 (lhs, rhs) {
case (.packet, .packet): return {
guard case .packet(let l) = lhs, case .packet(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.wantConfigID, .wantConfigID): return {
guard case .wantConfigID(let l) = lhs, case .wantConfigID(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.disconnect, .disconnect): return {
guard case .disconnect(let l) = lhs, case .disconnect(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.xmodemPacket, .xmodemPacket): return {
guard case .xmodemPacket(let l) = lhs, case .xmodemPacket(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.mqttClientProxyMessage, .mqttClientProxyMessage): return {
guard case .mqttClientProxyMessage(let l) = lhs, case .mqttClientProxyMessage(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.heartbeat, .heartbeat): return {
guard case .heartbeat(let l) = lhs, case .heartbeat(let r) = rhs else { preconditionFailure() }
return l == r
}()
default: return false
}
}
#endif
}
public init() {}
@ -3164,7 +2958,7 @@ public struct ToRadio {
///
/// Compressed message payload
public struct Compressed {
public struct Compressed: @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.
@ -3184,7 +2978,7 @@ public struct Compressed {
///
/// Full info on edges for a single node
public struct NeighborInfo {
public struct NeighborInfo: 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.
@ -3212,7 +3006,7 @@ public struct NeighborInfo {
///
/// A single edge in the mesh
public struct Neighbor {
public struct Neighbor: 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.
@ -3242,7 +3036,7 @@ public struct Neighbor {
///
/// Device metadata response
public struct DeviceMetadata {
public struct DeviceMetadata: 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.
@ -3304,7 +3098,7 @@ public struct DeviceMetadata {
///
/// A heartbeat message is sent to the node from the client to keep the connection alive.
/// This is currently only needed to keep serial connections alive, but can be used by any PhoneAPI.
public struct Heartbeat {
public struct Heartbeat: 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.
@ -3316,7 +3110,7 @@ public struct Heartbeat {
///
/// RemoteHardwarePins associated with a node
public struct NodeRemoteHardwarePin {
public struct NodeRemoteHardwarePin: 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.
@ -3343,7 +3137,7 @@ public struct NodeRemoteHardwarePin {
fileprivate var _pin: RemoteHardwarePin? = nil
}
public struct ChunkedPayload {
public struct ChunkedPayload: @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.
@ -3371,7 +3165,7 @@ public struct ChunkedPayload {
///
/// Wrapper message for broken repeated oneof support
public struct resend_chunks {
public struct resend_chunks: 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.
@ -3385,7 +3179,7 @@ public struct resend_chunks {
///
/// Responses to a ChunkedPayload request
public struct ChunkedPayloadResponse {
public struct ChunkedPayloadResponse: 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.
@ -3428,7 +3222,7 @@ public struct ChunkedPayloadResponse {
public var unknownFields = SwiftProtobuf.UnknownStorage()
public enum OneOf_PayloadVariant: Equatable {
public enum OneOf_PayloadVariant: Equatable, Sendable {
///
/// Request to transfer chunked payload
case requestTransfer(Bool)
@ -3439,77 +3233,11 @@ public struct ChunkedPayloadResponse {
/// Request missing indexes in the chunked payload
case resendChunks(resend_chunks)
#if !swift(>=4.1)
public static func ==(lhs: ChunkedPayloadResponse.OneOf_PayloadVariant, rhs: ChunkedPayloadResponse.OneOf_PayloadVariant) -> Bool {
// 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 (lhs, rhs) {
case (.requestTransfer, .requestTransfer): return {
guard case .requestTransfer(let l) = lhs, case .requestTransfer(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.acceptTransfer, .acceptTransfer): return {
guard case .acceptTransfer(let l) = lhs, case .acceptTransfer(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.resendChunks, .resendChunks): return {
guard case .resendChunks(let l) = lhs, case .resendChunks(let r) = rhs else { preconditionFailure() }
return l == r
}()
default: return false
}
}
#endif
}
public init() {}
}
#if swift(>=5.5) && canImport(_Concurrency)
extension HardwareModel: @unchecked Sendable {}
extension Constants: @unchecked Sendable {}
extension CriticalErrorCode: @unchecked Sendable {}
extension ExcludedModules: @unchecked Sendable {}
extension Position: @unchecked Sendable {}
extension Position.LocSource: @unchecked Sendable {}
extension Position.AltSource: @unchecked Sendable {}
extension User: @unchecked Sendable {}
extension RouteDiscovery: @unchecked Sendable {}
extension Routing: @unchecked Sendable {}
extension Routing.OneOf_Variant: @unchecked Sendable {}
extension Routing.Error: @unchecked Sendable {}
extension DataMessage: @unchecked Sendable {}
extension Waypoint: @unchecked Sendable {}
extension MqttClientProxyMessage: @unchecked Sendable {}
extension MqttClientProxyMessage.OneOf_PayloadVariant: @unchecked Sendable {}
extension MeshPacket: @unchecked Sendable {}
extension MeshPacket.OneOf_PayloadVariant: @unchecked Sendable {}
extension MeshPacket.Priority: @unchecked Sendable {}
extension MeshPacket.Delayed: @unchecked Sendable {}
extension NodeInfo: @unchecked Sendable {}
extension MyNodeInfo: @unchecked Sendable {}
extension LogRecord: @unchecked Sendable {}
extension LogRecord.Level: @unchecked Sendable {}
extension QueueStatus: @unchecked Sendable {}
extension FromRadio: @unchecked Sendable {}
extension FromRadio.OneOf_PayloadVariant: @unchecked Sendable {}
extension ClientNotification: @unchecked Sendable {}
extension FileInfo: @unchecked Sendable {}
extension ToRadio: @unchecked Sendable {}
extension ToRadio.OneOf_PayloadVariant: @unchecked Sendable {}
extension Compressed: @unchecked Sendable {}
extension NeighborInfo: @unchecked Sendable {}
extension Neighbor: @unchecked Sendable {}
extension DeviceMetadata: @unchecked Sendable {}
extension Heartbeat: @unchecked Sendable {}
extension NodeRemoteHardwarePin: @unchecked Sendable {}
extension ChunkedPayload: @unchecked Sendable {}
extension resend_chunks: @unchecked Sendable {}
extension ChunkedPayloadResponse: @unchecked Sendable {}
extension ChunkedPayloadResponse.OneOf_PayloadVariant: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "meshtastic"
@ -3604,6 +3332,7 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding {
85: .same(proto: "ROUTASTIC"),
86: .same(proto: "MESH_TAB"),
87: .same(proto: "MESHLINK"),
88: .same(proto: "XIAO_NRF52_KIT"),
255: .same(proto: "PRIVATE_HW"),
]
}
@ -4559,7 +4288,7 @@ extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio
if _storage._rxTime != 0 {
try visitor.visitSingularFixed32Field(value: _storage._rxTime, fieldNumber: 7)
}
if _storage._rxSnr != 0 {
if _storage._rxSnr.bitPattern != 0 {
try visitor.visitSingularFloatField(value: _storage._rxSnr, fieldNumber: 8)
}
if _storage._hopLimit != 0 {
@ -4761,7 +4490,7 @@ extension NodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
try { if let v = _storage._position {
try visitor.visitSingularMessageField(value: v, fieldNumber: 3)
} }()
if _storage._snr != 0 {
if _storage._snr.bitPattern != 0 {
try visitor.visitSingularFloatField(value: _storage._snr, fieldNumber: 4)
}
if _storage._lastHeard != 0 {
@ -5640,7 +5369,7 @@ extension Neighbor: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
if self.nodeID != 0 {
try visitor.visitSingularUInt32Field(value: self.nodeID, fieldNumber: 1)
}
if self.snr != 0 {
if self.snr.bitPattern != 0 {
try visitor.visitSingularFloatField(value: self.snr, fieldNumber: 2)
}
if self.lastRxTime != 0 {
@ -5765,8 +5494,8 @@ extension Heartbeat: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation
public static let _protobuf_nameMap = SwiftProtobuf._NameMap()
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let _ = try decoder.nextFieldNumber() {
}
// Load everything into unknown fields
while try decoder.nextFieldNumber() != nil {}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {

View file

@ -1,5 +1,6 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/module_config.proto
@ -7,7 +8,6 @@
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
@ -20,7 +20,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
typealias Version = _2
}
public enum RemoteHardwarePinType: SwiftProtobuf.Enum {
public enum RemoteHardwarePinType: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -58,24 +58,18 @@ public enum RemoteHardwarePinType: SwiftProtobuf.Enum {
}
}
}
#if swift(>=4.2)
extension RemoteHardwarePinType: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [RemoteHardwarePinType] = [
.unknown,
.digitalRead,
.digitalWrite,
]
}
#endif // swift(>=4.2)
}
///
/// Module Config
public struct ModuleConfig {
public struct ModuleConfig: 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.
@ -218,7 +212,7 @@ public struct ModuleConfig {
///
/// TODO: REPLACE
public enum OneOf_PayloadVariant: Equatable {
public enum OneOf_PayloadVariant: Equatable, Sendable {
///
/// TODO: REPLACE
case mqtt(ModuleConfig.MQTTConfig)
@ -259,73 +253,11 @@ public struct ModuleConfig {
/// TODO: REPLACE
case paxcounter(ModuleConfig.PaxcounterConfig)
#if !swift(>=4.1)
public static func ==(lhs: ModuleConfig.OneOf_PayloadVariant, rhs: ModuleConfig.OneOf_PayloadVariant) -> Bool {
// 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 (lhs, rhs) {
case (.mqtt, .mqtt): return {
guard case .mqtt(let l) = lhs, case .mqtt(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.serial, .serial): return {
guard case .serial(let l) = lhs, case .serial(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.externalNotification, .externalNotification): return {
guard case .externalNotification(let l) = lhs, case .externalNotification(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.storeForward, .storeForward): return {
guard case .storeForward(let l) = lhs, case .storeForward(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.rangeTest, .rangeTest): return {
guard case .rangeTest(let l) = lhs, case .rangeTest(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.telemetry, .telemetry): return {
guard case .telemetry(let l) = lhs, case .telemetry(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.cannedMessage, .cannedMessage): return {
guard case .cannedMessage(let l) = lhs, case .cannedMessage(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.audio, .audio): return {
guard case .audio(let l) = lhs, case .audio(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.remoteHardware, .remoteHardware): return {
guard case .remoteHardware(let l) = lhs, case .remoteHardware(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.neighborInfo, .neighborInfo): return {
guard case .neighborInfo(let l) = lhs, case .neighborInfo(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.ambientLighting, .ambientLighting): return {
guard case .ambientLighting(let l) = lhs, case .ambientLighting(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.detectionSensor, .detectionSensor): return {
guard case .detectionSensor(let l) = lhs, case .detectionSensor(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.paxcounter, .paxcounter): return {
guard case .paxcounter(let l) = lhs, case .paxcounter(let r) = rhs else { preconditionFailure() }
return l == r
}()
default: return false
}
}
#endif
}
///
/// MQTT Client Config
public struct MQTTConfig {
public struct MQTTConfig: 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.
@ -400,7 +332,7 @@ public struct ModuleConfig {
///
/// Settings for reporting unencrypted information about our node to a map via MQTT
public struct MapReportSettings {
public struct MapReportSettings: 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.
@ -420,7 +352,7 @@ public struct ModuleConfig {
///
/// RemoteHardwareModule Config
public struct RemoteHardwareConfig {
public struct RemoteHardwareConfig: 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.
@ -444,7 +376,7 @@ public struct ModuleConfig {
///
/// NeighborInfoModule Config
public struct NeighborInfoConfig {
public struct NeighborInfoConfig: 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.
@ -470,7 +402,7 @@ public struct ModuleConfig {
///
/// Detection Sensor Module Config
public struct DetectionSensorConfig {
public struct DetectionSensorConfig: 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.
@ -517,7 +449,7 @@ public struct ModuleConfig {
public var unknownFields = SwiftProtobuf.UnknownStorage()
public enum TriggerType: SwiftProtobuf.Enum {
public enum TriggerType: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
/// Event is triggered if pin is low
@ -569,6 +501,16 @@ public struct ModuleConfig {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [ModuleConfig.DetectionSensorConfig.TriggerType] = [
.logicLow,
.logicHigh,
.fallingEdge,
.risingEdge,
.eitherEdgeActiveLow,
.eitherEdgeActiveHigh,
]
}
public init() {}
@ -576,7 +518,7 @@ public struct ModuleConfig {
///
/// Audio Config for codec2 voice
public struct AudioConfig {
public struct AudioConfig: 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.
@ -613,7 +555,7 @@ public struct ModuleConfig {
///
/// Baudrate for codec2 voice
public enum Audio_Baud: SwiftProtobuf.Enum {
public enum Audio_Baud: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
case codec2Default // = 0
case codec23200 // = 1
@ -660,6 +602,19 @@ public struct ModuleConfig {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [ModuleConfig.AudioConfig.Audio_Baud] = [
.codec2Default,
.codec23200,
.codec22400,
.codec21600,
.codec21400,
.codec21300,
.codec21200,
.codec2700,
.codec2700B,
]
}
public init() {}
@ -667,7 +622,7 @@ public struct ModuleConfig {
///
/// Config for the Paxcounter Module
public struct PaxcounterConfig {
public struct PaxcounterConfig: 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.
@ -693,7 +648,7 @@ public struct ModuleConfig {
///
/// Serial Config
public struct SerialConfig {
public struct SerialConfig: 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.
@ -736,7 +691,7 @@ public struct ModuleConfig {
///
/// TODO: REPLACE
public enum Serial_Baud: SwiftProtobuf.Enum {
public enum Serial_Baud: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
case baudDefault // = 0
case baud110 // = 1
@ -804,11 +759,31 @@ public struct ModuleConfig {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [ModuleConfig.SerialConfig.Serial_Baud] = [
.baudDefault,
.baud110,
.baud300,
.baud600,
.baud1200,
.baud2400,
.baud4800,
.baud9600,
.baud19200,
.baud38400,
.baud57600,
.baud115200,
.baud230400,
.baud460800,
.baud576000,
.baud921600,
]
}
///
/// TODO: REPLACE
public enum Serial_Mode: SwiftProtobuf.Enum {
public enum Serial_Mode: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
case `default` // = 0
case simple // = 1
@ -853,6 +828,17 @@ public struct ModuleConfig {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [ModuleConfig.SerialConfig.Serial_Mode] = [
.default,
.simple,
.proto,
.textmsg,
.nmea,
.caltopo,
.ws85,
]
}
public init() {}
@ -860,7 +846,7 @@ public struct ModuleConfig {
///
/// External Notifications Config
public struct ExternalNotificationConfig {
public struct ExternalNotificationConfig: 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.
@ -943,7 +929,7 @@ public struct ModuleConfig {
///
/// Store and Forward Module Config
public struct StoreForwardConfig {
public struct StoreForwardConfig: 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.
@ -979,7 +965,7 @@ public struct ModuleConfig {
///
/// Preferences for the RangeTestModule
public struct RangeTestConfig {
public struct RangeTestConfig: 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.
@ -1004,7 +990,7 @@ public struct ModuleConfig {
///
/// Configuration for both device and environment metrics
public struct TelemetryConfig {
public struct TelemetryConfig: 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.
@ -1073,7 +1059,7 @@ public struct ModuleConfig {
///
/// Canned Messages Module Config
public struct CannedMessageConfig {
public struct CannedMessageConfig: 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.
@ -1128,7 +1114,7 @@ public struct ModuleConfig {
///
/// TODO: REPLACE
public enum InputEventChar: SwiftProtobuf.Enum {
public enum InputEventChar: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -1196,6 +1182,18 @@ public struct ModuleConfig {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [ModuleConfig.CannedMessageConfig.InputEventChar] = [
.none,
.up,
.down,
.left,
.right,
.select,
.back,
.cancel,
]
}
public init() {}
@ -1204,7 +1202,7 @@ public struct ModuleConfig {
///
///Ambient Lighting Module - Settings for control of onboard LEDs to allow users to adjust the brightness levels and respective color levels.
///Initially created for the RAK14001 RGB LED module.
public struct AmbientLightingConfig {
public struct AmbientLightingConfig: 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.
@ -1237,89 +1235,9 @@ public struct ModuleConfig {
public init() {}
}
#if swift(>=4.2)
extension ModuleConfig.DetectionSensorConfig.TriggerType: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [ModuleConfig.DetectionSensorConfig.TriggerType] = [
.logicLow,
.logicHigh,
.fallingEdge,
.risingEdge,
.eitherEdgeActiveLow,
.eitherEdgeActiveHigh,
]
}
extension ModuleConfig.AudioConfig.Audio_Baud: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [ModuleConfig.AudioConfig.Audio_Baud] = [
.codec2Default,
.codec23200,
.codec22400,
.codec21600,
.codec21400,
.codec21300,
.codec21200,
.codec2700,
.codec2700B,
]
}
extension ModuleConfig.SerialConfig.Serial_Baud: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [ModuleConfig.SerialConfig.Serial_Baud] = [
.baudDefault,
.baud110,
.baud300,
.baud600,
.baud1200,
.baud2400,
.baud4800,
.baud9600,
.baud19200,
.baud38400,
.baud57600,
.baud115200,
.baud230400,
.baud460800,
.baud576000,
.baud921600,
]
}
extension ModuleConfig.SerialConfig.Serial_Mode: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [ModuleConfig.SerialConfig.Serial_Mode] = [
.default,
.simple,
.proto,
.textmsg,
.nmea,
.caltopo,
.ws85,
]
}
extension ModuleConfig.CannedMessageConfig.InputEventChar: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [ModuleConfig.CannedMessageConfig.InputEventChar] = [
.none,
.up,
.down,
.left,
.right,
.select,
.back,
.cancel,
]
}
#endif // swift(>=4.2)
///
/// A GPIO pin definition for remote hardware module
public struct RemoteHardwarePin {
public struct RemoteHardwarePin: 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.
@ -1341,32 +1259,6 @@ public struct RemoteHardwarePin {
public init() {}
}
#if swift(>=5.5) && canImport(_Concurrency)
extension RemoteHardwarePinType: @unchecked Sendable {}
extension ModuleConfig: @unchecked Sendable {}
extension ModuleConfig.OneOf_PayloadVariant: @unchecked Sendable {}
extension ModuleConfig.MQTTConfig: @unchecked Sendable {}
extension ModuleConfig.MapReportSettings: @unchecked Sendable {}
extension ModuleConfig.RemoteHardwareConfig: @unchecked Sendable {}
extension ModuleConfig.NeighborInfoConfig: @unchecked Sendable {}
extension ModuleConfig.DetectionSensorConfig: @unchecked Sendable {}
extension ModuleConfig.DetectionSensorConfig.TriggerType: @unchecked Sendable {}
extension ModuleConfig.AudioConfig: @unchecked Sendable {}
extension ModuleConfig.AudioConfig.Audio_Baud: @unchecked Sendable {}
extension ModuleConfig.PaxcounterConfig: @unchecked Sendable {}
extension ModuleConfig.SerialConfig: @unchecked Sendable {}
extension ModuleConfig.SerialConfig.Serial_Baud: @unchecked Sendable {}
extension ModuleConfig.SerialConfig.Serial_Mode: @unchecked Sendable {}
extension ModuleConfig.ExternalNotificationConfig: @unchecked Sendable {}
extension ModuleConfig.StoreForwardConfig: @unchecked Sendable {}
extension ModuleConfig.RangeTestConfig: @unchecked Sendable {}
extension ModuleConfig.TelemetryConfig: @unchecked Sendable {}
extension ModuleConfig.CannedMessageConfig: @unchecked Sendable {}
extension ModuleConfig.CannedMessageConfig.InputEventChar: @unchecked Sendable {}
extension ModuleConfig.AmbientLightingConfig: @unchecked Sendable {}
extension RemoteHardwarePin: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "meshtastic"

View file

@ -1,5 +1,6 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/mqtt.proto
@ -7,7 +8,6 @@
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
///
/// This message wraps a MeshPacket with extra metadata about the sender and how it arrived.
public struct ServiceEnvelope {
public struct ServiceEnvelope: 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.
@ -57,7 +57,7 @@ public struct ServiceEnvelope {
///
/// Information about a node intended to be reported unencrypted to a map using MQTT.
public struct MapReport {
public struct MapReport: 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.
@ -121,11 +121,6 @@ public struct MapReport {
public init() {}
}
#if swift(>=5.5) && canImport(_Concurrency)
extension ServiceEnvelope: @unchecked Sendable {}
extension MapReport: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "meshtastic"

View file

@ -1,5 +1,6 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/paxcount.proto
@ -7,7 +8,6 @@
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
///
/// TODO: REPLACE
public struct Paxcount {
public struct Paxcount: 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.
@ -44,10 +44,6 @@ public struct Paxcount {
public init() {}
}
#if swift(>=5.5) && canImport(_Concurrency)
extension Paxcount: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "meshtastic"

View file

@ -1,5 +1,6 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/portnums.proto
@ -7,7 +8,6 @@
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
@ -33,7 +33,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
/// Note: This was formerly a Type enum named 'typ' with the same id #
/// We have change to this 'portnum' based scheme for specifying app handlers for particular payloads.
/// This change is backwards compatible by treating the legacy OPAQUE/CLEAR_TEXT values identically.
public enum PortNum: SwiftProtobuf.Enum {
public enum PortNum: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -193,6 +193,11 @@ public enum PortNum: SwiftProtobuf.Enum {
/// PowerStress based monitoring support (for automated power consumption testing)
case powerstressApp // = 74
///
/// Reticulum Network Stack Tunnel App
/// ENCODING: Fragmented RNS Packet. Handled by Meshtastic RNS interface
case reticulumTunnelApp // = 76
///
/// Private applications should use portnums >= 256.
/// To simplify initial development and testing you can use "PRIVATE_APP"
@ -241,6 +246,7 @@ public enum PortNum: SwiftProtobuf.Enum {
case 72: self = .atakPlugin
case 73: self = .mapReportApp
case 74: self = .powerstressApp
case 76: self = .reticulumTunnelApp
case 256: self = .privateApp
case 257: self = .atakForwarder
case 511: self = .max
@ -276,6 +282,7 @@ public enum PortNum: SwiftProtobuf.Enum {
case .atakPlugin: return 72
case .mapReportApp: return 73
case .powerstressApp: return 74
case .reticulumTunnelApp: return 76
case .privateApp: return 256
case .atakForwarder: return 257
case .max: return 511
@ -283,11 +290,6 @@ public enum PortNum: SwiftProtobuf.Enum {
}
}
}
#if swift(>=4.2)
extension PortNum: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [PortNum] = [
.unknownApp,
@ -316,18 +318,14 @@ extension PortNum: CaseIterable {
.atakPlugin,
.mapReportApp,
.powerstressApp,
.reticulumTunnelApp,
.privateApp,
.atakForwarder,
.max,
]
}
#endif // swift(>=4.2)
#if swift(>=5.5) && canImport(_Concurrency)
extension PortNum: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
extension PortNum: SwiftProtobuf._ProtoNameProviding {
@ -358,6 +356,7 @@ extension PortNum: SwiftProtobuf._ProtoNameProviding {
72: .same(proto: "ATAK_PLUGIN"),
73: .same(proto: "MAP_REPORT_APP"),
74: .same(proto: "POWERSTRESS_APP"),
76: .same(proto: "RETICULUM_TUNNEL_APP"),
256: .same(proto: "PRIVATE_APP"),
257: .same(proto: "ATAK_FORWARDER"),
511: .same(proto: "MAX"),

View file

@ -1,5 +1,6 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/powermon.proto
@ -7,7 +8,6 @@
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
@ -22,7 +22,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)
public struct PowerMon {
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
// methods supported on all messages.
@ -31,7 +31,7 @@ public struct PowerMon {
/// 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.
public enum State: SwiftProtobuf.Enum {
public enum State: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
case none // = 0
case cpuDeepSleep // = 1
@ -104,37 +104,31 @@ public struct PowerMon {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [PowerMon.State] = [
.none,
.cpuDeepSleep,
.cpuLightSleep,
.vext1On,
.loraRxon,
.loraTxon,
.loraRxactive,
.btOn,
.ledOn,
.screenOn,
.screenDrawing,
.wifiOn,
.gpsActive,
]
}
public init() {}
}
#if swift(>=4.2)
extension PowerMon.State: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [PowerMon.State] = [
.none,
.cpuDeepSleep,
.cpuLightSleep,
.vext1On,
.loraRxon,
.loraTxon,
.loraRxactive,
.btOn,
.ledOn,
.screenOn,
.screenDrawing,
.wifiOn,
.gpsActive,
]
}
#endif // swift(>=4.2)
///
/// PowerStress testing support via the C++ PowerStress module
public struct PowerStressMessage {
public struct PowerStressMessage: 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.
@ -151,7 +145,7 @@ public struct PowerStressMessage {
/// 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
public enum Opcode: SwiftProtobuf.Enum {
public enum Opcode: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -272,48 +266,35 @@ public struct PowerStressMessage {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [PowerStressMessage.Opcode] = [
.unset,
.printInfo,
.forceQuiet,
.endQuiet,
.screenOn,
.screenOff,
.cpuIdle,
.cpuDeepsleep,
.cpuFullon,
.ledOn,
.ledOff,
.loraOff,
.loraTx,
.loraRx,
.btOff,
.btOn,
.wifiOff,
.wifiOn,
.gpsOff,
.gpsOn,
]
}
public init() {}
}
#if swift(>=4.2)
extension PowerStressMessage.Opcode: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [PowerStressMessage.Opcode] = [
.unset,
.printInfo,
.forceQuiet,
.endQuiet,
.screenOn,
.screenOff,
.cpuIdle,
.cpuDeepsleep,
.cpuFullon,
.ledOn,
.ledOff,
.loraOff,
.loraTx,
.loraRx,
.btOff,
.btOn,
.wifiOff,
.wifiOn,
.gpsOff,
.gpsOn,
]
}
#endif // swift(>=4.2)
#if swift(>=5.5) && canImport(_Concurrency)
extension PowerMon: @unchecked Sendable {}
extension PowerMon.State: @unchecked Sendable {}
extension PowerStressMessage: @unchecked Sendable {}
extension PowerStressMessage.Opcode: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "meshtastic"
@ -323,8 +304,8 @@ extension PowerMon: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
public static let _protobuf_nameMap = SwiftProtobuf._NameMap()
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let _ = try decoder.nextFieldNumber() {
}
// Load everything into unknown fields
while try decoder.nextFieldNumber() != nil {}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
@ -379,7 +360,7 @@ extension PowerStressMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
if self.cmd != .unset {
try visitor.visitSingularEnumField(value: self.cmd, fieldNumber: 1)
}
if self.numSeconds != 0 {
if self.numSeconds.bitPattern != 0 {
try visitor.visitSingularFloatField(value: self.numSeconds, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)

View file

@ -1,5 +1,6 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/remote_hardware.proto
@ -7,7 +8,6 @@
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
@ -30,7 +30,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
/// because no security yet (beyond the channel mechanism).
/// It should be off by default and then protected based on some TBD mechanism
/// (a special channel once multichannel support is included?)
public struct HardwareMessage {
public struct HardwareMessage: 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.
@ -52,7 +52,7 @@ public struct HardwareMessage {
///
/// TODO: REPLACE
public enum TypeEnum: SwiftProtobuf.Enum {
public enum TypeEnum: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -110,32 +110,21 @@ public struct HardwareMessage {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [HardwareMessage.TypeEnum] = [
.unset,
.writeGpios,
.watchGpios,
.gpiosChanged,
.readGpios,
.readGpiosReply,
]
}
public init() {}
}
#if swift(>=4.2)
extension HardwareMessage.TypeEnum: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [HardwareMessage.TypeEnum] = [
.unset,
.writeGpios,
.watchGpios,
.gpiosChanged,
.readGpios,
.readGpiosReply,
]
}
#endif // swift(>=4.2)
#if swift(>=5.5) && canImport(_Concurrency)
extension HardwareMessage: @unchecked Sendable {}
extension HardwareMessage.TypeEnum: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "meshtastic"

View file

@ -1,5 +1,6 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/rtttl.proto
@ -7,7 +8,6 @@
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
///
/// Canned message module configuration.
public struct RTTTLConfig {
public struct RTTTLConfig: 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.
@ -36,10 +36,6 @@ public struct RTTTLConfig {
public init() {}
}
#if swift(>=5.5) && canImport(_Concurrency)
extension RTTTLConfig: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "meshtastic"

View file

@ -1,5 +1,6 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/storeforward.proto
@ -22,7 +23,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
///
/// TODO: REPLACE
public struct StoreAndForward {
public struct StoreAndForward: @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.
@ -79,7 +80,7 @@ public struct StoreAndForward {
///
/// TODO: REPLACE
public enum OneOf_Variant: Equatable {
public enum OneOf_Variant: Equatable, @unchecked Sendable {
///
/// TODO: REPLACE
case stats(StoreAndForward.Statistics)
@ -93,38 +94,12 @@ public struct StoreAndForward {
/// Text from history message.
case text(Data)
#if !swift(>=4.1)
public static func ==(lhs: StoreAndForward.OneOf_Variant, rhs: StoreAndForward.OneOf_Variant) -> Bool {
// 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 (lhs, rhs) {
case (.stats, .stats): return {
guard case .stats(let l) = lhs, case .stats(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.history, .history): return {
guard case .history(let l) = lhs, case .history(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.heartbeat, .heartbeat): return {
guard case .heartbeat(let l) = lhs, case .heartbeat(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.text, .text): return {
guard case .text(let l) = lhs, case .text(let r) = rhs else { preconditionFailure() }
return l == r
}()
default: return false
}
}
#endif
}
///
/// 001 - 063 = From Router
/// 064 - 127 = From Client
public enum RequestResponse: SwiftProtobuf.Enum {
public enum RequestResponse: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -242,11 +217,31 @@ public struct StoreAndForward {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [StoreAndForward.RequestResponse] = [
.unset,
.routerError,
.routerHeartbeat,
.routerPing,
.routerPong,
.routerBusy,
.routerHistory,
.routerStats,
.routerTextDirect,
.routerTextBroadcast,
.clientError,
.clientHistory,
.clientStats,
.clientPing,
.clientPong,
.clientAbort,
]
}
///
/// TODO: REPLACE
public struct Statistics {
public struct Statistics: 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.
@ -294,7 +289,7 @@ public struct StoreAndForward {
///
/// TODO: REPLACE
public struct History {
public struct History: 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.
@ -319,7 +314,7 @@ public struct StoreAndForward {
///
/// TODO: REPLACE
public struct Heartbeat {
public struct Heartbeat: 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.
@ -340,41 +335,6 @@ public struct StoreAndForward {
public init() {}
}
#if swift(>=4.2)
extension StoreAndForward.RequestResponse: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [StoreAndForward.RequestResponse] = [
.unset,
.routerError,
.routerHeartbeat,
.routerPing,
.routerPong,
.routerBusy,
.routerHistory,
.routerStats,
.routerTextDirect,
.routerTextBroadcast,
.clientError,
.clientHistory,
.clientStats,
.clientPing,
.clientPong,
.clientAbort,
]
}
#endif // swift(>=4.2)
#if swift(>=5.5) && canImport(_Concurrency)
extension StoreAndForward: @unchecked Sendable {}
extension StoreAndForward.OneOf_Variant: @unchecked Sendable {}
extension StoreAndForward.RequestResponse: @unchecked Sendable {}
extension StoreAndForward.Statistics: @unchecked Sendable {}
extension StoreAndForward.History: @unchecked Sendable {}
extension StoreAndForward.Heartbeat: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "meshtastic"

View file

@ -1,5 +1,6 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/telemetry.proto
@ -7,7 +8,6 @@
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
///
/// Supported I2C Sensors for telemetry in Meshtastic
public enum TelemetrySensorType: SwiftProtobuf.Enum {
public enum TelemetrySensorType: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
///
@ -122,7 +122,7 @@ public enum TelemetrySensorType: SwiftProtobuf.Enum {
case aht10 // = 23
///
/// DFRobot Lark Weather station (temperature, humidity, pressure, wind speed and direction)
/// DFRobot Lark Weather station (temperature, humidity, pressure, wind speed and direction)
case dfrobotLark // = 24
///
@ -146,7 +146,7 @@ public enum TelemetrySensorType: SwiftProtobuf.Enum {
case customSensor // = 29
///
/// MAX30102 Pulse Oximeter and Heart-Rate Sensor
/// MAX30102 Pulse Oximeter and Heart-Rate Sensor
case max30102 // = 30
///
@ -168,6 +168,14 @@ public enum TelemetrySensorType: SwiftProtobuf.Enum {
///
/// DFRobot Gravity tipping bucket rain gauge
case dfrobotRain // = 35
///
/// Infineon DPS310 High accuracy pressure and temperature
case dps310 // = 36
///
/// RAKWireless RAK12035 Soil Moisture Sensor Module
case rak12035 // = 37
case UNRECOGNIZED(Int)
public init() {
@ -212,6 +220,8 @@ public enum TelemetrySensorType: SwiftProtobuf.Enum {
case 33: self = .radsens
case 34: self = .ina226
case 35: self = .dfrobotRain
case 36: self = .dps310
case 37: self = .rak12035
default: self = .UNRECOGNIZED(rawValue)
}
}
@ -254,15 +264,12 @@ public enum TelemetrySensorType: SwiftProtobuf.Enum {
case .radsens: return 33
case .ina226: return 34
case .dfrobotRain: return 35
case .dps310: return 36
case .rak12035: return 37
case .UNRECOGNIZED(let i): return i
}
}
}
#if swift(>=4.2)
extension TelemetrySensorType: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [TelemetrySensorType] = [
.sensorUnset,
@ -301,14 +308,15 @@ extension TelemetrySensorType: CaseIterable {
.radsens,
.ina226,
.dfrobotRain,
.dps310,
.rak12035,
]
}
#endif // swift(>=4.2)
}
///
/// Key native device metrics such as battery level
public struct DeviceMetrics {
public struct DeviceMetrics: 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.
@ -381,7 +389,7 @@ public struct DeviceMetrics {
///
/// Weather station or other environmental metrics
public struct EnvironmentMetrics {
public struct EnvironmentMetrics: @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.
@ -452,7 +460,7 @@ public struct EnvironmentMetrics {
/// Clears the value of `current`. Subsequent reads from it will return its default value.
public mutating func clearCurrent() {_uniqueStorage()._current = nil}
///
///
/// relative scale IAQ value as measured by Bosch BME680 . value 0-500.
/// Belongs to Air Quality but is not particle but VOC measurement. Other VOC values can also be put in here.
public var iaq: UInt32 {
@ -608,6 +616,28 @@ public struct EnvironmentMetrics {
/// Clears the value of `rainfall24H`. Subsequent reads from it will return its default value.
public mutating func clearRainfall24H() {_uniqueStorage()._rainfall24H = nil}
///
/// Soil moisture measured (% 1-100)
public var soilMoisture: UInt32 {
get {return _storage._soilMoisture ?? 0}
set {_uniqueStorage()._soilMoisture = newValue}
}
/// Returns true if `soilMoisture` has been explicitly set.
public var hasSoilMoisture: Bool {return _storage._soilMoisture != nil}
/// Clears the value of `soilMoisture`. Subsequent reads from it will return its default value.
public mutating func clearSoilMoisture() {_uniqueStorage()._soilMoisture = nil}
///
/// Soil temperature measured (*C)
public var soilTemperature: Float {
get {return _storage._soilTemperature ?? 0}
set {_uniqueStorage()._soilTemperature = newValue}
}
/// Returns true if `soilTemperature` has been explicitly set.
public var hasSoilTemperature: Bool {return _storage._soilTemperature != nil}
/// Clears the value of `soilTemperature`. Subsequent reads from it will return its default value.
public mutating func clearSoilTemperature() {_uniqueStorage()._soilTemperature = nil}
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
@ -617,7 +647,7 @@ public struct EnvironmentMetrics {
///
/// Power Metrics (voltage / current / etc)
public struct PowerMetrics {
public struct PowerMetrics: 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.
@ -702,7 +732,7 @@ public struct PowerMetrics {
///
/// Air quality metrics
public struct AirQualityMetrics {
public struct AirQualityMetrics: 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.
@ -871,7 +901,7 @@ public struct AirQualityMetrics {
///
/// Local device mesh statistics
public struct LocalStats {
public struct LocalStats: 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.
@ -929,7 +959,7 @@ public struct LocalStats {
///
/// Health telemetry metrics
public struct HealthMetrics {
public struct HealthMetrics: 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.
@ -978,7 +1008,7 @@ public struct HealthMetrics {
///
/// Types of Measurements the telemetry module is equipped to handle
public struct Telemetry {
public struct Telemetry: 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.
@ -1051,7 +1081,7 @@ public struct Telemetry {
public var unknownFields = SwiftProtobuf.UnknownStorage()
public enum OneOf_Variant: Equatable {
public enum OneOf_Variant: Equatable, Sendable {
///
/// Key native device metrics such as battery level
case deviceMetrics(DeviceMetrics)
@ -1071,40 +1101,6 @@ public struct Telemetry {
/// Health telemetry metrics
case healthMetrics(HealthMetrics)
#if !swift(>=4.1)
public static func ==(lhs: Telemetry.OneOf_Variant, rhs: Telemetry.OneOf_Variant) -> Bool {
// 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 (lhs, rhs) {
case (.deviceMetrics, .deviceMetrics): return {
guard case .deviceMetrics(let l) = lhs, case .deviceMetrics(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.environmentMetrics, .environmentMetrics): return {
guard case .environmentMetrics(let l) = lhs, case .environmentMetrics(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.airQualityMetrics, .airQualityMetrics): return {
guard case .airQualityMetrics(let l) = lhs, case .airQualityMetrics(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.powerMetrics, .powerMetrics): return {
guard case .powerMetrics(let l) = lhs, case .powerMetrics(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.localStats, .localStats): return {
guard case .localStats(let l) = lhs, case .localStats(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.healthMetrics, .healthMetrics): return {
guard case .healthMetrics(let l) = lhs, case .healthMetrics(let r) = rhs else { preconditionFailure() }
return l == r
}()
default: return false
}
}
#endif
}
public init() {}
@ -1112,7 +1108,7 @@ public struct Telemetry {
///
/// NAU7802 Telemetry configuration, for saving to flash
public struct Nau7802Config {
public struct Nau7802Config: 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.
@ -1130,19 +1126,6 @@ public struct Nau7802Config {
public init() {}
}
#if swift(>=5.5) && canImport(_Concurrency)
extension TelemetrySensorType: @unchecked Sendable {}
extension DeviceMetrics: @unchecked Sendable {}
extension EnvironmentMetrics: @unchecked Sendable {}
extension PowerMetrics: @unchecked Sendable {}
extension AirQualityMetrics: @unchecked Sendable {}
extension LocalStats: @unchecked Sendable {}
extension HealthMetrics: @unchecked Sendable {}
extension Telemetry: @unchecked Sendable {}
extension Telemetry.OneOf_Variant: @unchecked Sendable {}
extension Nau7802Config: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "meshtastic"
@ -1185,6 +1168,8 @@ extension TelemetrySensorType: SwiftProtobuf._ProtoNameProviding {
33: .same(proto: "RADSENS"),
34: .same(proto: "INA226"),
35: .same(proto: "DFROBOT_RAIN"),
36: .same(proto: "DPS310"),
37: .same(proto: "RAK12035"),
]
}
@ -1271,6 +1256,8 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
18: .same(proto: "radiation"),
19: .standard(proto: "rainfall_1h"),
20: .standard(proto: "rainfall_24h"),
21: .standard(proto: "soil_moisture"),
22: .standard(proto: "soil_temperature"),
]
fileprivate class _StorageClass {
@ -1294,6 +1281,8 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
var _radiation: Float? = nil
var _rainfall1H: Float? = nil
var _rainfall24H: Float? = nil
var _soilMoisture: UInt32? = nil
var _soilTemperature: Float? = nil
#if swift(>=5.10)
// This property is used as the initial default value for new instances of the type.
@ -1328,6 +1317,8 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
_radiation = source._radiation
_rainfall1H = source._rainfall1H
_rainfall24H = source._rainfall24H
_soilMoisture = source._soilMoisture
_soilTemperature = source._soilTemperature
}
}
@ -1366,6 +1357,8 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
case 18: try { try decoder.decodeSingularFloatField(value: &_storage._radiation) }()
case 19: try { try decoder.decodeSingularFloatField(value: &_storage._rainfall1H) }()
case 20: try { try decoder.decodeSingularFloatField(value: &_storage._rainfall24H) }()
case 21: try { try decoder.decodeSingularUInt32Field(value: &_storage._soilMoisture) }()
case 22: try { try decoder.decodeSingularFloatField(value: &_storage._soilTemperature) }()
default: break
}
}
@ -1438,6 +1431,12 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
try { if let v = _storage._rainfall24H {
try visitor.visitSingularFloatField(value: v, fieldNumber: 20)
} }()
try { if let v = _storage._soilMoisture {
try visitor.visitSingularUInt32Field(value: v, fieldNumber: 21)
} }()
try { if let v = _storage._soilTemperature {
try visitor.visitSingularFloatField(value: v, fieldNumber: 22)
} }()
}
try unknownFields.traverse(visitor: &visitor)
}
@ -1467,6 +1466,8 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
if _storage._radiation != rhs_storage._radiation {return false}
if _storage._rainfall1H != rhs_storage._rainfall1H {return false}
if _storage._rainfall24H != rhs_storage._rainfall24H {return false}
if _storage._soilMoisture != rhs_storage._soilMoisture {return false}
if _storage._soilTemperature != rhs_storage._soilTemperature {return false}
return true
}
if !storagesAreEqual {return false}
@ -1692,10 +1693,10 @@ extension LocalStats: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio
if self.uptimeSeconds != 0 {
try visitor.visitSingularUInt32Field(value: self.uptimeSeconds, fieldNumber: 1)
}
if self.channelUtilization != 0 {
if self.channelUtilization.bitPattern != 0 {
try visitor.visitSingularFloatField(value: self.channelUtilization, fieldNumber: 2)
}
if self.airUtilTx != 0 {
if self.airUtilTx.bitPattern != 0 {
try visitor.visitSingularFloatField(value: self.airUtilTx, fieldNumber: 3)
}
if self.numPacketsTx != 0 {
@ -1962,7 +1963,7 @@ extension Nau7802Config: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa
if self.zeroOffset != 0 {
try visitor.visitSingularInt32Field(value: self.zeroOffset, fieldNumber: 1)
}
if self.calibrationFactor != 0 {
if self.calibrationFactor.bitPattern != 0 {
try visitor.visitSingularFloatField(value: self.calibrationFactor, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)

View file

@ -1,5 +1,6 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/xmodem.proto
@ -20,7 +21,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
typealias Version = _2
}
public struct XModem {
public struct XModem: @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.
@ -35,7 +36,7 @@ public struct XModem {
public var unknownFields = SwiftProtobuf.UnknownStorage()
public enum Control: SwiftProtobuf.Enum {
public enum Control: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
case nul // = 0
case soh // = 1
@ -79,34 +80,23 @@ public struct XModem {
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [XModem.Control] = [
.nul,
.soh,
.stx,
.eot,
.ack,
.nak,
.can,
.ctrlz,
]
}
public init() {}
}
#if swift(>=4.2)
extension XModem.Control: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [XModem.Control] = [
.nul,
.soh,
.stx,
.eot,
.ack,
.nak,
.can,
.ctrlz,
]
}
#endif // swift(>=4.2)
#if swift(>=5.5) && canImport(_Concurrency)
extension XModem: @unchecked Sendable {}
extension XModem.Control: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "meshtastic"

View file

@ -9,16 +9,22 @@ SwiftUI client applications for iOS, iPadOS and macOS.
This project always uses the latest release version of XCode.
1. Clone the repo.
2. Set up git hooks to automatically lint the project when you commit changes.
2. Open `Meshtastic.xcworkspace`
2. Build and run the `Meshtastic` target.
```sh
git clone git@github.com:meshtastic/Meshtastic-Apple.git
cd Meshtastic-Apple
./scripts/setup-hooks.sh
open Meshtastic.xcworkspace
```
```sh
git clone git@github.com:meshtastic/Meshtastic-Apple.git
```
2. Open the local directory.
```sh
cd Meshtastic-Apple
```
3. Set up git hooks to automatically lint the project when you commit changes.
```sh
./scripts/setup-hooks.sh
```
4. Open `Meshtastic.xcworkspace`
```sh
open Meshtastic.xcworkspace
```
5. Build and run the `Meshtastic` target.
## Technical Standards