forked from fritob/Camper-Monitor
Komplikation fürs Zifferblatt, und die App heisst jetzt VanControl Pro
Zwei Dinge in einem Commit, weil sie sich dieselben Dateien teilen: project.pbxproj und WatchRootView.swift tragen beides. **Komplikation.** Neues Target CamperMonitorComplication, auf watchOS sind Komplikationen WidgetKit-Widgets. Es wird in die Watch-App eingebettet (PlugIns) und bietet die vier Zubehör-Familien an. Ein Tipp öffnet die App direkt auf der Nivellierung statt in der Geräteliste: Die Komplikation hängt eine Adresse an, die Watch-App führt dafür jetzt einen Navigationsweg, den sie von aussen setzen kann. Die Adresse steht in Shared/WatchDeepLink.swift, weil beide Seiten sie brauchen – ein Tippfehler auf einer Seite bliebe sonst stumm. Bewusst ohne Messwert: Eine Komplikation läuft in einem eigenen Prozess und käme an die Werte nur über eine App-Gruppe, und die verlangt eine bezahlte Mitgliedschaft. Ein Wert, der beim Blick aufs Zifferblatt Stunden alt sein kann, wäre ohnehin schlimmer als keiner – man glaubt ihm. Die Komplikation tut deshalb genau eine Sache, und die zuverlässig. **Rebranding.** Der sichtbare Name ist überall VanControl Pro: CFBundleDisplayName beider Apple-Targets, Android-Label, die Texte in beiden Apps, beide READMEs und der Name der APK im Gitea-Ablauf. Die Kennungen bleiben, wie sie sind – Bundle-IDs, Android-Paket, Target-, Ordner- und Schemanamen. Eine geänderte Bundle-ID ist für iOS eine neue App: Geräte, Fahrzeugprofile und Victron-Schlüssel wären weg, dazu neue Profile und eine neu zu koppelnde Watch-App. Sichtbar ist davon nichts, also gibt es dafür auch keinen Grund. Geprüft: alle drei Apple-Targets bauen, die Erweiterung liegt im Watch-Bundle unter PlugIns, die Anzeigenamen stimmen im gebauten Bundle, Tests grün. Die neue Bundle-ID der Erweiterung braucht beim ersten Lauf aus Xcode ein Profil, das Xcode selbst anlegt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -59,7 +59,7 @@ jobs:
|
|||||||
-PversionName="$VERSION" \
|
-PversionName="$VERSION" \
|
||||||
-PversionCode="$CODE" --no-daemon
|
-PversionCode="$CODE" --no-daemon
|
||||||
mv app/build/outputs/apk/release/app-release.apk \
|
mv app/build/outputs/apk/release/app-release.apk \
|
||||||
"../CamperMonitor-$VERSION.apk"
|
"../VanControlPro-$VERSION.apk"
|
||||||
|
|
||||||
- name: An das Release hängen
|
- name: An das Release hängen
|
||||||
env:
|
env:
|
||||||
@@ -88,7 +88,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
curl -sf -X POST -H "$AUTH" \
|
curl -sf -X POST -H "$AUTH" \
|
||||||
-F "attachment=@CamperMonitor-$VERSION.apk" \
|
-F "attachment=@VanControlPro-$VERSION.apk" \
|
||||||
"$API/$ID/assets?name=CamperMonitor-$VERSION.apk" > /dev/null
|
"$API/$ID/assets?name=VanControlPro-$VERSION.apk" > /dev/null
|
||||||
|
|
||||||
echo "CamperMonitor-$VERSION.apk hängt an Release $GITHUB_REF_NAME."
|
echo "VanControlPro-$VERSION.apk hängt an Release $GITHUB_REF_NAME."
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ fun WithBluetoothPermission(content: @Composable () -> Unit) {
|
|||||||
if (asked) {
|
if (asked) {
|
||||||
Text(
|
Text(
|
||||||
"Die Freigabe wurde abgelehnt. Sie lässt sich in den " +
|
"Die Freigabe wurde abgelehnt. Sie lässt sich in den " +
|
||||||
"Android-Einstellungen unter „Apps → Camper Monitor → " +
|
"Android-Einstellungen unter „Apps → VanControl Pro → " +
|
||||||
"Berechtigungen“ nachholen.",
|
"Berechtigungen“ nachholen.",
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
color = MaterialTheme.colorScheme.error,
|
color = MaterialTheme.colorScheme.error,
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ fun SettingsScreen(store: DeviceStore, onBack: () -> Unit) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
"Camper Monitor liest Victron-Geräte über ihr Advertisement mit " +
|
"VanControl Pro liest Victron-Geräte über ihr Advertisement mit " +
|
||||||
"(„Instant Readout“) und spricht Batterie, Kühlbox und " +
|
"(„Instant Readout“) und spricht Batterie, Kühlbox und " +
|
||||||
"Neigungsmesser direkt über Bluetooth an.",
|
"Neigungsmesser direkt über Bluetooth an.",
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Camper Monitor</string>
|
<string name="app_name">VanControl Pro</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
AA0000000000000000000027 /* CamperMonitorWatch.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = AA0000000000000000000016 /* CamperMonitorWatch.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
AA0000000000000000000027 /* CamperMonitorWatch.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = AA0000000000000000000016 /* CamperMonitorWatch.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
|
AA0000000000000000000042 /* CamperMonitorComplication.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = AA0000000000000000000032 /* CamperMonitorComplication.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -18,6 +19,13 @@
|
|||||||
remoteGlobalIDString = AA0000000000000000000022;
|
remoteGlobalIDString = AA0000000000000000000022;
|
||||||
remoteInfo = CamperMonitorWatch;
|
remoteInfo = CamperMonitorWatch;
|
||||||
};
|
};
|
||||||
|
AA0000000000000000000043 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = AA0000000000000000000009 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = AA0000000000000000000037;
|
||||||
|
remoteInfo = CamperMonitorComplication;
|
||||||
|
};
|
||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
@@ -32,12 +40,25 @@
|
|||||||
name = "Embed Watch Content";
|
name = "Embed Watch Content";
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
AA0000000000000000000041 /* Embed Foundation Extensions */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 13;
|
||||||
|
files = (
|
||||||
|
AA0000000000000000000042 /* CamperMonitorComplication.appex in Embed Foundation Extensions */,
|
||||||
|
);
|
||||||
|
name = "Embed Foundation Extensions";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
AA0000000000000000000001 /* CamperMonitor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CamperMonitor.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
AA0000000000000000000001 /* CamperMonitor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CamperMonitor.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
AA0000000000000000000016 /* CamperMonitorWatch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CamperMonitorWatch.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
AA0000000000000000000016 /* CamperMonitorWatch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CamperMonitorWatch.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
AA0000000000000000000030 /* CamperMonitor-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CamperMonitor-Info.plist"; sourceTree = "<group>"; };
|
AA0000000000000000000030 /* CamperMonitor-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CamperMonitor-Info.plist"; sourceTree = "<group>"; };
|
||||||
|
AA0000000000000000000045 /* CamperMonitorComplication-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CamperMonitorComplication-Info.plist"; sourceTree = "<group>"; };
|
||||||
|
AA0000000000000000000032 /* CamperMonitorComplication.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = CamperMonitorComplication.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||||
@@ -51,6 +72,11 @@
|
|||||||
path = CamperMonitorWatch;
|
path = CamperMonitorWatch;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
AA0000000000000000000033 /* CamperMonitorComplication */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
|
path = CamperMonitorComplication;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
AA0000000000000000000018 /* Shared */ = {
|
AA0000000000000000000018 /* Shared */ = {
|
||||||
isa = PBXFileSystemSynchronizedRootGroup;
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
path = Shared;
|
path = Shared;
|
||||||
@@ -73,6 +99,13 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
AA0000000000000000000035 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
@@ -81,6 +114,7 @@
|
|||||||
children = (
|
children = (
|
||||||
AA0000000000000000000002 /* CamperMonitor */,
|
AA0000000000000000000002 /* CamperMonitor */,
|
||||||
AA0000000000000000000017 /* CamperMonitorWatch */,
|
AA0000000000000000000017 /* CamperMonitorWatch */,
|
||||||
|
AA0000000000000000000033 /* CamperMonitorComplication */,
|
||||||
AA0000000000000000000018 /* Shared */,
|
AA0000000000000000000018 /* Shared */,
|
||||||
AA0000000000000000000031 /* Config */,
|
AA0000000000000000000031 /* Config */,
|
||||||
AA0000000000000000000005 /* Products */,
|
AA0000000000000000000005 /* Products */,
|
||||||
@@ -92,6 +126,7 @@
|
|||||||
children = (
|
children = (
|
||||||
AA0000000000000000000001 /* CamperMonitor.app */,
|
AA0000000000000000000001 /* CamperMonitor.app */,
|
||||||
AA0000000000000000000016 /* CamperMonitorWatch.app */,
|
AA0000000000000000000016 /* CamperMonitorWatch.app */,
|
||||||
|
AA0000000000000000000032 /* CamperMonitorComplication.appex */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -100,6 +135,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
AA0000000000000000000030 /* CamperMonitor-Info.plist */,
|
AA0000000000000000000030 /* CamperMonitor-Info.plist */,
|
||||||
|
AA0000000000000000000045 /* CamperMonitorComplication-Info.plist */,
|
||||||
);
|
);
|
||||||
path = Config;
|
path = Config;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -137,10 +173,12 @@
|
|||||||
AA0000000000000000000019 /* Sources */,
|
AA0000000000000000000019 /* Sources */,
|
||||||
AA0000000000000000000020 /* Frameworks */,
|
AA0000000000000000000020 /* Frameworks */,
|
||||||
AA0000000000000000000021 /* Resources */,
|
AA0000000000000000000021 /* Resources */,
|
||||||
|
AA0000000000000000000041 /* Embed Foundation Extensions */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
|
AA0000000000000000000044 /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
fileSystemSynchronizedGroups = (
|
fileSystemSynchronizedGroups = (
|
||||||
AA0000000000000000000017 /* CamperMonitorWatch */,
|
AA0000000000000000000017 /* CamperMonitorWatch */,
|
||||||
@@ -151,6 +189,27 @@
|
|||||||
productReference = AA0000000000000000000016 /* CamperMonitorWatch.app */;
|
productReference = AA0000000000000000000016 /* CamperMonitorWatch.app */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.application";
|
||||||
};
|
};
|
||||||
|
AA0000000000000000000037 /* CamperMonitorComplication */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = AA0000000000000000000040 /* Build configuration list for PBXNativeTarget "CamperMonitorComplication" */;
|
||||||
|
buildPhases = (
|
||||||
|
AA0000000000000000000034 /* Sources */,
|
||||||
|
AA0000000000000000000035 /* Frameworks */,
|
||||||
|
AA0000000000000000000036 /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
fileSystemSynchronizedGroups = (
|
||||||
|
AA0000000000000000000033 /* CamperMonitorComplication */,
|
||||||
|
AA0000000000000000000018 /* Shared */,
|
||||||
|
);
|
||||||
|
name = CamperMonitorComplication;
|
||||||
|
productName = CamperMonitorComplication;
|
||||||
|
productReference = AA0000000000000000000032 /* CamperMonitorComplication.appex */;
|
||||||
|
productType = "com.apple.product-type.app-extension";
|
||||||
|
};
|
||||||
/* End PBXNativeTarget section */
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
/* Begin PBXProject section */
|
||||||
@@ -167,6 +226,9 @@
|
|||||||
AA0000000000000000000022 = {
|
AA0000000000000000000022 = {
|
||||||
CreatedOnToolsVersion = 26.6;
|
CreatedOnToolsVersion = 26.6;
|
||||||
};
|
};
|
||||||
|
AA0000000000000000000037 = {
|
||||||
|
CreatedOnToolsVersion = 26.6;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = AA0000000000000000000010 /* Build configuration list for PBXProject "CamperMonitor" */;
|
buildConfigurationList = AA0000000000000000000010 /* Build configuration list for PBXProject "CamperMonitor" */;
|
||||||
@@ -186,6 +248,7 @@
|
|||||||
targets = (
|
targets = (
|
||||||
AA0000000000000000000006 /* CamperMonitor */,
|
AA0000000000000000000006 /* CamperMonitor */,
|
||||||
AA0000000000000000000022 /* CamperMonitorWatch */,
|
AA0000000000000000000022 /* CamperMonitorWatch */,
|
||||||
|
AA0000000000000000000037 /* CamperMonitorComplication */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
@@ -205,6 +268,13 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
AA0000000000000000000036 /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
@@ -222,6 +292,13 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
AA0000000000000000000034 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXTargetDependency section */
|
/* Begin PBXTargetDependency section */
|
||||||
@@ -231,6 +308,11 @@
|
|||||||
target = AA0000000000000000000022 /* CamperMonitorWatch */;
|
target = AA0000000000000000000022 /* CamperMonitorWatch */;
|
||||||
targetProxy = AA0000000000000000000028 /* PBXContainerItemProxy */;
|
targetProxy = AA0000000000000000000028 /* PBXContainerItemProxy */;
|
||||||
};
|
};
|
||||||
|
AA0000000000000000000044 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = AA0000000000000000000037 /* CamperMonitorComplication */;
|
||||||
|
targetProxy = AA0000000000000000000043 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
/* End PBXTargetDependency section */
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
@@ -301,7 +383,7 @@
|
|||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = "Config/CamperMonitor-Info.plist";
|
INFOPLIST_FILE = "Config/CamperMonitor-Info.plist";
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "Camper Monitor";
|
INFOPLIST_KEY_CFBundleDisplayName = "VanControl Pro";
|
||||||
INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "Zum Auslesen von Victron-Geraeten und dem Daly BMS per Bluetooth.";
|
INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "Zum Auslesen von Victron-Geraeten und dem Daly BMS per Bluetooth.";
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
@@ -332,7 +414,7 @@
|
|||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = "Config/CamperMonitor-Info.plist";
|
INFOPLIST_FILE = "Config/CamperMonitor-Info.plist";
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "Camper Monitor";
|
INFOPLIST_KEY_CFBundleDisplayName = "VanControl Pro";
|
||||||
INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "Zum Auslesen von Victron-Geraeten und dem Daly BMS per Bluetooth.";
|
INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "Zum Auslesen von Victron-Geraeten und dem Daly BMS per Bluetooth.";
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
@@ -361,7 +443,7 @@
|
|||||||
DEVELOPMENT_TEAM = 6AP73NYV5W;
|
DEVELOPMENT_TEAM = 6AP73NYV5W;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "Camper Monitor";
|
INFOPLIST_KEY_CFBundleDisplayName = "VanControl Pro";
|
||||||
INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "Zum Auslesen des Neigungsmessers direkt an der Uhr, ohne Umweg ueber das iPhone.";
|
INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "Zum Auslesen des Neigungsmessers direkt an der Uhr, ohne Umweg ueber das iPhone.";
|
||||||
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = de.fritob.CamperMonitor;
|
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = de.fritob.CamperMonitor;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@@ -393,7 +475,7 @@
|
|||||||
DEVELOPMENT_TEAM = 6AP73NYV5W;
|
DEVELOPMENT_TEAM = 6AP73NYV5W;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "Camper Monitor";
|
INFOPLIST_KEY_CFBundleDisplayName = "VanControl Pro";
|
||||||
INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "Zum Auslesen des Neigungsmessers direkt an der Uhr, ohne Umweg ueber das iPhone.";
|
INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "Zum Auslesen des Neigungsmessers direkt an der Uhr, ohne Umweg ueber das iPhone.";
|
||||||
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = de.fritob.CamperMonitor;
|
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = de.fritob.CamperMonitor;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@@ -415,6 +497,64 @@
|
|||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
AA0000000000000000000038 = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = 6AP73NYV5W;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = "Config/CamperMonitorComplication-Info.plist";
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = "Nivellierung";
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@executable_path/../../Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = de.fritob.CamperMonitor.watchkitapp.complication;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = watchos;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SUPPORTED_PLATFORMS = "watchos watchsimulator";
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = 4;
|
||||||
|
WATCHOS_DEPLOYMENT_TARGET = 10.0;
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
AA0000000000000000000039 = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = 6AP73NYV5W;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = "Config/CamperMonitorComplication-Info.plist";
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = "Nivellierung";
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@executable_path/../../Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = de.fritob.CamperMonitor.watchkitapp.complication;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = watchos;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SUPPORTED_PLATFORMS = "watchos watchsimulator";
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = 4;
|
||||||
|
WATCHOS_DEPLOYMENT_TARGET = 10.0;
|
||||||
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
@@ -445,6 +585,15 @@
|
|||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
|
AA0000000000000000000040 /* Build configuration list for PBXNativeTarget "CamperMonitorComplication" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
AA0000000000000000000038 /* Debug */,
|
||||||
|
AA0000000000000000000039 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
};
|
};
|
||||||
rootObject = AA0000000000000000000009 /* Project object */;
|
rootObject = AA0000000000000000000009 /* Project object */;
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import WidgetKit
|
||||||
|
|
||||||
|
/// Komplikation fürs Zifferblatt: ein Tipp, und die Nivellierung steht da.
|
||||||
|
///
|
||||||
|
/// Bewusst **ohne Messwert**. Eine Komplikation läuft in einem eigenen Prozess
|
||||||
|
/// und käme an die Werte nur über eine App-Gruppe – die verlangt eine bezahlte
|
||||||
|
/// Entwicklermitgliedschaft. Ein Wert, der beim Blick aufs Zifferblatt
|
||||||
|
/// womöglich Stunden alt ist, wäre ausserdem schlimmer als keiner: Man würde
|
||||||
|
/// ihm glauben.
|
||||||
|
///
|
||||||
|
/// Die Komplikation tut deshalb genau eine Sache, und die zuverlässig: Sie
|
||||||
|
/// öffnet die App auf der Nivellierung. Von dort sind die Werte live, weil die
|
||||||
|
/// Uhr den Sensor selbst anfunkt.
|
||||||
|
struct LevelComplicationEntry: TimelineEntry {
|
||||||
|
let date: Date
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Nichts zu planen: Die Anzeige ändert sich nie, also ein einziger Eintrag
|
||||||
|
/// ohne Nachschub. Das kostet die Uhr auch nichts.
|
||||||
|
struct LevelComplicationProvider: TimelineProvider {
|
||||||
|
func placeholder(in context: Context) -> LevelComplicationEntry {
|
||||||
|
LevelComplicationEntry(date: .now)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSnapshot(in context: Context,
|
||||||
|
completion: @escaping (LevelComplicationEntry) -> Void) {
|
||||||
|
completion(LevelComplicationEntry(date: .now))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTimeline(in context: Context,
|
||||||
|
completion: @escaping (Timeline<LevelComplicationEntry>) -> Void) {
|
||||||
|
completion(Timeline(entries: [LevelComplicationEntry(date: .now)], policy: .never))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LevelComplicationView: View {
|
||||||
|
@Environment(\.widgetFamily) private var family
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
switch family {
|
||||||
|
case .accessoryInline:
|
||||||
|
// Eine Zeile Text, mehr lässt diese Familie nicht zu.
|
||||||
|
Label("Nivellierung", systemImage: "level")
|
||||||
|
|
||||||
|
case .accessoryCorner:
|
||||||
|
Image(systemName: "level")
|
||||||
|
.font(.title2)
|
||||||
|
.widgetLabel("Nivellieren")
|
||||||
|
|
||||||
|
case .accessoryRectangular:
|
||||||
|
HStack(spacing: 6) {
|
||||||
|
Image(systemName: "level")
|
||||||
|
.font(.title3)
|
||||||
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
|
Text("Nivellierung")
|
||||||
|
.font(.headline)
|
||||||
|
Text("Antippen zum Ausrichten")
|
||||||
|
.font(.caption2)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Rund – auf dem Zifferblatt der knappste Platz, also nur das
|
||||||
|
// Symbol, gross genug zum Treffen.
|
||||||
|
ZStack {
|
||||||
|
AccessoryWidgetBackground()
|
||||||
|
Image(systemName: "level")
|
||||||
|
.font(.title2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@main
|
||||||
|
struct LevelComplication: Widget {
|
||||||
|
var body: some WidgetConfiguration {
|
||||||
|
StaticConfiguration(kind: "de.fritob.CamperMonitor.complication.level",
|
||||||
|
provider: LevelComplicationProvider()) { _ in
|
||||||
|
LevelComplicationView()
|
||||||
|
// Der Tipp landet nicht irgendwo in der App, sondern auf der
|
||||||
|
// Nivellierung – dafür wertet die App diese Adresse aus.
|
||||||
|
.widgetURL(WatchDeepLink.level)
|
||||||
|
.containerBackground(.clear, for: .widget)
|
||||||
|
}
|
||||||
|
.configurationDisplayName("Nivellierung")
|
||||||
|
.description("Öffnet die Nivellierung direkt vom Zifferblatt.")
|
||||||
|
.supportedFamilies([.accessoryCircular, .accessoryCorner,
|
||||||
|
.accessoryInline, .accessoryRectangular])
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
/// Camper Monitor am Handgelenk.
|
/// VanControl Pro am Handgelenk.
|
||||||
///
|
///
|
||||||
/// Zwei Quellen, jede dort, wo sie hingehört:
|
/// Zwei Quellen, jede dort, wo sie hingehört:
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -41,9 +41,7 @@ struct WatchLevelView: View {
|
|||||||
.padding(.top, 2)
|
.padding(.top, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationLink {
|
NavigationLink(value: WatchRoute.align) {
|
||||||
WatchAlignView()
|
|
||||||
} label: {
|
|
||||||
Label("Ausrichten", systemImage: "arrow.left.and.right")
|
Label("Ausrichten", systemImage: "arrow.left.and.right")
|
||||||
}
|
}
|
||||||
.buttonStyle(.borderedProminent)
|
.buttonStyle(.borderedProminent)
|
||||||
|
|||||||
@@ -6,10 +6,28 @@ struct WatchRootView: View {
|
|||||||
@Environment(PhoneLink.self) private var link
|
@Environment(PhoneLink.self) private var link
|
||||||
@Environment(WatchLevelRadio.self) private var radio
|
@Environment(WatchLevelRadio.self) private var radio
|
||||||
|
|
||||||
|
/// Der Weg durch die Ansichten liegt hier, damit ihn auch die Komplikation
|
||||||
|
/// setzen kann: Ein Tipp aufs Zifferblatt springt direkt zur Nivellierung,
|
||||||
|
/// statt in der Geräteliste zu landen.
|
||||||
|
@State private var path: [WatchRoute] = []
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack {
|
NavigationStack(path: $path) {
|
||||||
content
|
content
|
||||||
.navigationTitle(link.payload?.profile.name ?? "Camper")
|
.navigationTitle(link.payload?.profile.name ?? "Camper")
|
||||||
|
.navigationDestination(for: WatchRoute.self) { route in
|
||||||
|
switch route {
|
||||||
|
case .level: WatchLevelView()
|
||||||
|
case .align: WatchAlignView()
|
||||||
|
case .device(let id): WatchDeviceDetailView(deviceID: id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onOpenURL { url in
|
||||||
|
guard WatchDeepLink.isLevel(url) else { return }
|
||||||
|
// Nicht anhängen, sondern ersetzen: Wer vom Zifferblatt kommt, will
|
||||||
|
// die Nivellierung sehen und nicht einen Stapel von vorhin.
|
||||||
|
path = [.level]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,9 +36,7 @@ struct WatchRootView: View {
|
|||||||
// Die Nivellierung steht immer oben und immer da: Sie kommt vom
|
// Die Nivellierung steht immer oben und immer da: Sie kommt vom
|
||||||
// Sensor selbst und braucht das iPhone nicht.
|
// Sensor selbst und braucht das iPhone nicht.
|
||||||
Section {
|
Section {
|
||||||
NavigationLink {
|
NavigationLink(value: WatchRoute.level) {
|
||||||
WatchLevelView()
|
|
||||||
} label: {
|
|
||||||
LevelSummaryRow(reading: WatchLevelReading(radio: radio, phone: link))
|
LevelSummaryRow(reading: WatchLevelReading(radio: radio, phone: link))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,9 +44,7 @@ struct WatchRootView: View {
|
|||||||
if let payload = link.payload {
|
if let payload = link.payload {
|
||||||
Section {
|
Section {
|
||||||
ForEach(payload.devices.filter { $0.role != .leveling }) { device in
|
ForEach(payload.devices.filter { $0.role != .leveling }) { device in
|
||||||
NavigationLink {
|
NavigationLink(value: WatchRoute.device(device.id)) {
|
||||||
WatchDeviceDetailView(deviceID: device.id)
|
|
||||||
} label: {
|
|
||||||
DeviceRow(device: device)
|
DeviceRow(device: device)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,7 +69,7 @@ struct WatchRootView: View {
|
|||||||
VStack(spacing: 4) {
|
VStack(spacing: 4) {
|
||||||
Label("Warte auf das iPhone", systemImage: "iphone.gen3.radiowaves.left.and.right")
|
Label("Warte auf das iPhone", systemImage: "iphone.gen3.radiowaves.left.and.right")
|
||||||
.font(.footnote.weight(.medium))
|
.font(.footnote.weight(.medium))
|
||||||
Text("Dort muss Camper Monitor geöffnet sein.")
|
Text("Dort muss VanControl Pro geöffnet sein.")
|
||||||
.font(.caption2)
|
.font(.caption2)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
}
|
}
|
||||||
@@ -63,6 +77,14 @@ struct WatchRootView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wohin die Uhr springen kann. Als eigener Typ, weil der Weg von aussen
|
||||||
|
/// gesetzt wird – von der Komplikation.
|
||||||
|
enum WatchRoute: Hashable {
|
||||||
|
case level
|
||||||
|
case align
|
||||||
|
case device(UUID)
|
||||||
|
}
|
||||||
|
|
||||||
/// Eine Gerätezeile: Name klein, Hauptwert gross – auf dem kleinen Bildschirm
|
/// Eine Gerätezeile: Name klein, Hauptwert gross – auf dem kleinen Bildschirm
|
||||||
/// zählt nur der eine Wert, wegen dem man den Arm hebt.
|
/// zählt nur der eine Wert, wegen dem man den Arm hebt.
|
||||||
struct DeviceRow: View {
|
struct DeviceRow: View {
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?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>
|
||||||
|
<key>NSExtension</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
<string>com.apple.widgetkit-extension</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Camper Monitor
|
# VanControl Pro
|
||||||
|
|
||||||
iOS-App, die per Bluetooth LE die Energieanlage im Wohnmobil ausliest:
|
iOS-App, die per Bluetooth LE die Energieanlage im Wohnmobil ausliest:
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ Protokoll erkannt wurde, steht in der Detailansicht unter **Diagnose** –
|
|||||||
zusammen mit der letzten Rohantwort.
|
zusammen mit der letzten Rohantwort.
|
||||||
|
|
||||||
Nur **eine** App gleichzeitig kann mit dem BMS verbunden sein. Wenn die
|
Nur **eine** App gleichzeitig kann mit dem BMS verbunden sein. Wenn die
|
||||||
Hersteller-App offen ist, bekommt Camper Monitor keine Verbindung.
|
Hersteller-App offen ist, bekommt VanControl Pro keine Verbindung.
|
||||||
|
|
||||||
### Kühlbox
|
### Kühlbox
|
||||||
|
|
||||||
@@ -305,7 +305,7 @@ auch dort nur, was das Gerät zurückmeldet, nicht der Tastendruck.
|
|||||||
|
|
||||||
### Was das für den Betrieb heisst
|
### Was das für den Betrieb heisst
|
||||||
|
|
||||||
Für Batterie, Solar und Kühlbox muss Camper Monitor auf dem iPhone laufen.
|
Für Batterie, Solar und Kühlbox muss VanControl Pro auf dem iPhone laufen.
|
||||||
Solange die Uhr meldet, dass jemand hinschaut, hält die App das Funkgerät auch
|
Solange die Uhr meldet, dass jemand hinschaut, hält die App das Funkgerät auch
|
||||||
im Hintergrund am Leben; die Victron-Werbedaten stehen dabei still, weil iOS im
|
im Hintergrund am Leben; die Victron-Werbedaten stehen dabei still, weil iOS im
|
||||||
Hintergrund kein ungefiltertes Suchen erlaubt.
|
Hintergrund kein ungefiltertes Suchen erlaubt.
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Die Adressen, über die etwas in der Watch-App geöffnet wird.
|
||||||
|
///
|
||||||
|
/// Sie stehen hier, weil beide Seiten sie brauchen und sie sonst zweimal
|
||||||
|
/// dastünden: Die Komplikation hängt sie an ihre Anzeige, die App wertet sie
|
||||||
|
/// beim Öffnen aus. Ein Tippfehler auf einer Seite wäre stumm – der Tipp aufs
|
||||||
|
/// Zifferblatt öffnete dann einfach die Geräteliste.
|
||||||
|
enum WatchDeepLink {
|
||||||
|
private static let scheme = "campermonitor"
|
||||||
|
|
||||||
|
/// Führt direkt auf die Nivellierung.
|
||||||
|
static let level = URL(string: "\(scheme)://nivellierung")!
|
||||||
|
|
||||||
|
/// Ob diese Adresse zur Nivellierung führt.
|
||||||
|
static func isLevel(_ url: URL) -> Bool {
|
||||||
|
url.scheme == scheme && (url.host() == "nivellierung")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,7 +26,7 @@ darüber auffindbar).
|
|||||||
| Roll | `…3425` | lesen | Querneigung, Float32 little-endian, Grad |
|
| Roll | `…3425` | lesen | Querneigung, Float32 little-endian, Grad |
|
||||||
| Kalibrieren | `…3427` | schreiben | ein Byte: `0` setzt zurück, alles andere kalibriert |
|
| Kalibrieren | `…3427` | schreiben | ein Byte: `0` setzt zurück, alles andere kalibriert |
|
||||||
|
|
||||||
Der Client fragt Pitch und Roll im Takt ab; Camper Monitor tut das zweimal je
|
Der Client fragt Pitch und Roll im Takt ab; VanControl Pro tut das zweimal je
|
||||||
Sekunde.
|
Sekunde.
|
||||||
|
|
||||||
Positiver Pitch heißt: das Heck steht höher. Positiver Roll: die rechte Seite
|
Positiver Pitch heißt: das Heck steht höher. Positiver Roll: die rechte Seite
|
||||||
@@ -35,7 +35,7 @@ steht höher.
|
|||||||
### Kalibrierung
|
### Kalibrierung
|
||||||
|
|
||||||
Fahrzeug eben stellen, dann ein beliebiges Byte ungleich `0` auf `…3427`
|
Fahrzeug eben stellen, dann ein beliebiges Byte ungleich `0` auf `…3427`
|
||||||
schreiben – oder in Camper Monitor auf *Auf aktuelle Lage kalibrieren* tippen.
|
schreiben – oder in VanControl Pro auf *Auf aktuelle Lage kalibrieren* tippen.
|
||||||
Die Offsets werden dauerhaft gespeichert und überstehen einen Neustart.
|
Die Offsets werden dauerhaft gespeichert und überstehen einen Neustart.
|
||||||
|
|
||||||
## Was sich gegenüber 1.0 geändert hat
|
## Was sich gegenüber 1.0 geändert hat
|
||||||
@@ -70,7 +70,7 @@ Die UUIDs sind unverändert, die vorhandene WebApp läuft weiter.
|
|||||||
|
|
||||||
## Clients
|
## Clients
|
||||||
|
|
||||||
* **Camper Monitor** (iOS) – die App in diesem Projekt. Geräteart
|
* **VanControl Pro** (iOS) – die App in diesem Projekt. Geräteart
|
||||||
*Nivellierung*: grafische Libelle, Kalibrierung aus der App, zusammen mit
|
*Nivellierung*: grafische Libelle, Kalibrierung aus der App, zusammen mit
|
||||||
den übrigen Geräten im Fahrzeug.
|
den übrigen Geräten im Fahrzeug.
|
||||||
* Die ursprüngliche WebBLE-Oberfläche liegt weiterhin im Projekt
|
* Die ursprüngliche WebBLE-Oberfläche liegt weiterhin im Projekt
|
||||||
|
|||||||
Reference in New Issue
Block a user